diff --git a/.agents/skills/observing-misaligned-landings/SKILL.md b/.agents/skills/observing-misaligned-landings/SKILL.md index 59466389..27e5e341 100644 --- a/.agents/skills/observing-misaligned-landings/SKILL.md +++ b/.agents/skills/observing-misaligned-landings/SKILL.md @@ -100,21 +100,29 @@ corepack pnpm@10.20.0 --dir "$SKILL" install --frozen-lockfile node "$SKILL/scripts/interpret-anomalies.mjs" /tmp/misaligned-landing.json ``` -The interpreter uses `@letta-ai/letta-agent-sdk` to find exactly one retained -`letta/auto` specialist named `Misaligned Landing Observer`, provisioning it only when -absent. More than one exact-name match fails closed. The specialist is hidden from -ordinary agent surfaces but remains available to exact-name API lookup, with empty -memory, MemFS disabled, no tools or skills, and a strict zero-action prompt. +The interpreter uses `@letta-ai/letta-agent-sdk` to exhaust the exact-name API lookup +and find exactly one retained `letta/auto` specialist named +`Misaligned Landing Observer`, provisioning it only when absent. More than one +exact-name match, incomplete pagination, or pagination that does not advance fails +closed. Every reuse validates the returned agent's exact name and type, hidden state, +pinned model, zero-action system prompt, sole MemFS-disabled origin tag, empty memory, +and absence of attached sources or tools. A newly created specialist is re-read and +must satisfy the same complete state before its identity is trusted. The specialist +remains hidden from ordinary agent surfaces but available to exact-name API lookup. + Each observation uses SDK `prompt()` with `stateless: true`, which opens a fresh conversation for that retained agent rather than resuming an earlier receipt and -loads no persisted MemFS state. Only minimized evidence -crosses the repository boundary: local paths, remote URLs, task paths, and raw read -errors are removed. Interpretation may explain likely convergence, stale evidence, or -project-operation inconsistency, but cannot alter the receipt or its deterministic -state. The pinned transport contract resolves one explicit organization computer, -opens separate control and stream sockets, starts a strict stateless runtime with no -skills, synchronizes it, and sends the turn with empty client-tool authority and -interactive tools excluded. +loads no persisted MemFS state. Only minimized evidence crosses the repository +boundary: local paths, remote URLs, task paths, and raw read errors are removed. +Interpretation may explain likely convergence, stale evidence, or project-operation +inconsistency, but cannot alter the receipt or its deterministic state. Computer +selection is explicit: `LETTA_COMPUTER_NAME` selects one named computer through the +SDK's ambiguity- and offline-checking resolver; without it, discovery must completely +enumerate exactly one online organization computer and binds the prompt to that +computer's stable device id. Zero, multiple, offline, malformed, or incompletely +enumerated candidates fail closed. The pinned transport then opens separate control +and stream sockets, starts a strict stateless runtime with no skills, synchronizes it, +and sends the turn with empty client-tool authority and interactive tools excluded. If no non-benign anomaly remains, the interpreter returns `status: not-needed` without loading the SDK or contacting Letta. If credentials, SDK installation, exact @@ -131,8 +139,9 @@ corepack pnpm@10.20.0 --dir .agents/skills/observing-misaligned-landings test The fixture proves byte-stable clean receipts, exact anomaly codes and dirty-path ownership, complete Telegram status/message-id semantics, a clean descendant after -candidate cleanup, optional-SDK fail-closed behavior, the real pinned SDK's -REST/WebSocket request shape through a hermetic fake transport, and that observing -leaves the target repository's Git bytes unchanged. The project gate performs the frozen install before +candidate cleanup, optional-SDK fail-closed behavior, exhaustive and state-validating +specialist lookup, explicit computer selection, the real pinned SDK's REST/WebSocket +request shape through a hermetic fake transport, and that observing leaves the target +repository's Git bytes unchanged. The project gate performs the frozen install before the Node suite; missing dependencies cannot silently turn transport coverage into a skip. diff --git a/.agents/skills/observing-misaligned-landings/scripts/interpret-anomalies.mjs b/.agents/skills/observing-misaligned-landings/scripts/interpret-anomalies.mjs index 76fd6649..dd15179f 100755 --- a/.agents/skills/observing-misaligned-landings/scripts/interpret-anomalies.mjs +++ b/.agents/skills/observing-misaligned-landings/scripts/interpret-anomalies.mjs @@ -9,16 +9,22 @@ export const INTERPRETATION_SCHEMA = "network.comind.misaligned.landing-interpretation/v1"; export const MODEL = "letta/auto"; export const SPECIALIST_NAME = "Misaligned Landing Observer"; +export const SPECIALIST_SYSTEM_PROMPT = [ + "You are the retained Misaligned Landing Observer interpreting exactly one minimized deterministic landing receipt in a fresh stateless conversation.", + "You have no tools, skills, repository access, or authority to mutate anything.", + "The supplied classifier state is authoritative and immutable; explain anomalies without changing that state, acting, or promising action.", +].join(" "); const MAX_RECEIPT_BYTES = 256 * 1024; const BENIGN_ANOMALY_CODES = new Set([ "current_main_unpublished", "public_edge_converging", ]); -const INTERPRETER_SYSTEM_PROMPT = [ - "You are the retained Misaligned Landing Observer interpreting exactly one minimized deterministic landing receipt in a fresh stateless conversation.", - "You have no tools, skills, repository access, or authority to mutate anything.", - "The supplied classifier state is authoritative and immutable; explain anomalies without changing that state, acting, or promising action.", -].join(" "); +const SPECIALIST_LIST_QUERY = { + include: ["agent.blocks", "agent.sources", "agent.tags", "agent.tools"], + limit: 100, + name: SPECIALIST_NAME, + show_hidden_agents: true, +}; function validateFacts(value, field) { if (!Array.isArray(value)) { @@ -208,31 +214,83 @@ async function readReceipt(path) { } function agentId(agent) { - return agent && typeof agent.id === "string" && agent.id.length > 0 + return agent && + typeof agent.id === "string" && + agent.id.length > 0 && + agent.id === agent.id.trim() ? agent.id : null; } -export async function findOrCreateSpecialist(client, lookup = client.agents) { - const page = await lookup.list({ - limit: 100, - name: SPECIALIST_NAME, - show_hidden_agents: true, - }); - const listed = Array.isArray(page) ? page : page?.items; - if (!Array.isArray(listed)) { +function pageItems(page) { + const items = page?.items; + if ( + Array.isArray(page) || + !Array.isArray(items) || + typeof page?.hasNextPage !== "function" || + typeof page?.getNextPage !== "function" + ) { throw new Error("Letta agent lookup returned an unsupported result"); } - const exact = listed.filter((agent) => agent?.name === SPECIALIST_NAME); - if (exact.length > 1) { - throw new Error(`more than one Letta agent is named ${SPECIALIST_NAME}`); + return items; +} + +export function validateSpecialistState(agent) { + const id = agentId(agent); + const tags = Array.isArray(agent?.tags) ? [...agent.tags].sort() : null; + const failures = []; + if (!id) failures.push("usable agent id"); + if (agent?.name !== SPECIALIST_NAME) failures.push("exact name"); + if (agent?.agent_type !== "letta_v1_agent") failures.push("Letta Code agent type"); + if (agent?.hidden !== true) failures.push("hidden state"); + if (agent?.model !== MODEL) failures.push("pinned model"); + if (agent?.system !== SPECIALIST_SYSTEM_PROMPT) failures.push("zero-authority system prompt"); + if (!Array.isArray(agent?.blocks) || agent.blocks.length !== 0) { + failures.push("empty memory blocks"); + } + if (!Array.isArray(agent?.sources) || agent.sources.length !== 0) { + failures.push("no attached sources"); } - if (exact.length === 1) { - const existingId = agentId(exact[0]); - if (!existingId) { - throw new Error("the retained landing observer has no usable agent id"); + if (!Array.isArray(agent?.tools) || agent.tools.length !== 0) { + failures.push("no attached tools"); + } + if (!tags || tags.length !== 1 || tags[0] !== "origin:letta-code") { + failures.push("MemFS-disabled origin tags"); + } + if (failures.length > 0) { + throw new Error( + `the retained landing observer does not satisfy: ${failures.join(", ")}`, + ); + } + return agent; +} + +async function exactSpecialists(lookup) { + let page = await lookup.list(SPECIALIST_LIST_QUERY); + const exact = []; + const pageCursors = new Set(); + while (true) { + const items = pageItems(page); + exact.push(...items.filter((agent) => agent?.name === SPECIALIST_NAME)); + if (exact.length > 1) { + throw new Error(`more than one Letta agent is named ${SPECIALIST_NAME}`); + } + if (!page.hasNextPage()) break; + const cursor = agentId(items.at(-1)); + if (!cursor || pageCursors.has(cursor)) { + throw new Error("Letta agent lookup pagination did not advance"); } - return { agentId: existingId, created: false }; + pageCursors.add(cursor); + page = await page.getNextPage(); + } + return exact; +} + +export async function findOrCreateSpecialist(client, lookup = client.agents) { + const exact = await exactSpecialists(lookup); + if (exact.length === 1) { + const existing = validateSpecialistState(exact[0]); + return { agentId: agentId(existing), created: false }; } const createdId = await client.createAgent({ baseTools: [], @@ -243,14 +301,53 @@ export async function findOrCreateSpecialist(client, lookup = client.agents) { memory: [], model: MODEL, name: SPECIALIST_NAME, - systemPrompt: INTERPRETER_SYSTEM_PROMPT, + systemPrompt: SPECIALIST_SYSTEM_PROMPT, }); - if (typeof createdId !== "string" || createdId.length === 0) { + if ( + typeof createdId !== "string" || + createdId.length === 0 || + createdId !== createdId.trim() + ) { throw new Error("creating the retained landing observer returned no usable agent id"); } + const afterCreate = await exactSpecialists(lookup); + if (afterCreate.length !== 1 || agentId(afterCreate[0]) !== createdId) { + throw new Error("the retained landing observer could not be uniquely re-read after creation"); + } + validateSpecialistState(afterCreate[0]); return { agentId: createdId, created: true }; } +export async function selectComputer(client, env = process.env) { + const configuredName = env.LETTA_COMPUTER_NAME?.trim(); + if (configuredName) { + return { name: configuredName }; + } + const result = await client.computers.list({ limit: 100, onlineOnly: true }); + if ( + !result || + !Array.isArray(result.computers) || + result.hasNextPage !== false || + result.computers.some((computer) => computer?.status !== "online") + ) { + throw new Error("online Letta computer discovery returned incomplete evidence"); + } + if (result.computers.length !== 1) { + throw new Error( + `expected exactly one online Letta computer, found ${result.computers.length}; set LETTA_COMPUTER_NAME to select one explicitly`, + ); + } + const deviceId = result.computers[0]?.deviceId; + if ( + typeof deviceId !== "string" || + deviceId.length === 0 || + deviceId !== deviceId.trim() + ) { + throw new Error("the selected online Letta computer has no stable device id"); + } + return { deviceId }; +} + export async function interpret( receipt, env = process.env, @@ -287,9 +384,11 @@ export async function interpret( ...(apiKey ? { apiKey } : {}), ...(env.LETTA_BASE_URL ? { baseURL: env.LETTA_BASE_URL } : {}), }); + const computer = await selectComputer(client, env); const specialist = await findOrCreateSpecialist(client, lookup.agents); const result = await client.prompt(buildPrompt(validated), specialist.agentId, { allowedTools: [], + computer, permissionMode: "strict", skillSources: [], stateless: true, diff --git a/.agents/skills/observing-misaligned-landings/tests/interpret-anomalies.test.mjs b/.agents/skills/observing-misaligned-landings/tests/interpret-anomalies.test.mjs index 8bf65f78..341a4297 100644 --- a/.agents/skills/observing-misaligned-landings/tests/interpret-anomalies.test.mjs +++ b/.agents/skills/observing-misaligned-landings/tests/interpret-anomalies.test.mjs @@ -6,14 +6,46 @@ import { INTERPRETATION_SCHEMA, MODEL, SPECIALIST_NAME, + SPECIALIST_SYSTEM_PROMPT, buildPrompt, findOrCreateSpecialist, interpretationReceipt, interpret, needsInterpretation, + selectComputer, + validateSpecialistState, validateReceipt, } from "../scripts/interpret-anomalies.mjs"; +function specialistState(overrides = {}) { + return { + agent_type: "letta_v1_agent", + blocks: [], + hidden: true, + id: "agent-observer", + model: MODEL, + name: SPECIALIST_NAME, + sources: [], + system: SPECIALIST_SYSTEM_PROMPT, + tags: ["origin:letta-code"], + tools: [], + ...overrides, + }; +} + +function agentPage(items, next = null) { + return { + items, + hasNextPage() { + return next !== null; + }, + async getNextPage() { + assert.ok(next); + return next; + }, + }; +} + function receipt(overrides = {}) { return { schema: "network.comind.misaligned.landing-observation/v2", @@ -131,10 +163,10 @@ test("specialist lookup reuses the exact retained name", async () => { agents: { async list(query) { calls.push(["list", query]); - return [ + return agentPage([ { id: "agent-near", name: `${SPECIALIST_NAME} copy` }, - { id: "agent-exact", name: SPECIALIST_NAME }, - ]; + specialistState({ id: "agent-exact" }), + ]); }, }, async createAgent() { @@ -149,18 +181,29 @@ test("specialist lookup reuses the exact retained name", async () => { assert.deepEqual(calls, [ [ "list", - { limit: 100, name: SPECIALIST_NAME, show_hidden_agents: true }, + { + include: ["agent.blocks", "agent.sources", "agent.tags", "agent.tools"], + limit: 100, + name: SPECIALIST_NAME, + show_hidden_agents: true, + }, ], ]); }); test("specialist creation is empty-memory, zero-tool, and persistent", async () => { const calls = []; + let lookupCount = 0; const client = { agents: { async list(query) { calls.push(["list", query]); - return []; + lookupCount += 1; + return agentPage( + lookupCount === 1 + ? [] + : [specialistState({ id: "agent-created" })], + ); }, }, async createAgent(options) { @@ -190,6 +233,167 @@ test("specialist creation is empty-memory, zero-tool, and persistent", async () "systemPrompt", ]); assert.equal(calls.some(([kind]) => kind === "delete"), false); + assert.equal(calls.filter(([kind]) => kind === "list").length, 2); +}); + +test("specialist reuse fails closed when retained state gains authority", () => { + for (const [field, value] of [ + ["id", ""], + ["id", " "], + ["name", "Another observer"], + ["agent_type", "memgpt_agent"], + ["hidden", false], + ["model", "letta/another-model"], + ["blocks", [{ id: "block-one" }]], + ["sources", [{ id: "folder-one" }]], + ["tools", [{ id: "tool-one" }]], + ["tags", ["git-memory-enabled", "origin:letta-code"]], + ["system", "A different prompt"], + ]) { + assert.throws( + () => validateSpecialistState(specialistState({ [field]: value })), + /retained landing observer does not satisfy/, + field, + ); + } +}); + +test("specialist lookup enumerates every filtered page before reuse", async () => { + const second = agentPage([specialistState({ id: "agent-second" })]); + const first = agentPage( + [{ id: "agent-first", name: `${SPECIALIST_NAME} copy` }], + second, + ); + const result = await findOrCreateSpecialist({ + agents: { async list() { return first; } }, + }); + assert.deepEqual(result, { agentId: "agent-second", created: false }); + const duplicateSecond = agentPage([ + specialistState({ id: "agent-second" }), + ]); + const duplicateFirst = agentPage( + [specialistState({ id: "agent-first" })], + duplicateSecond, + ); + await assert.rejects( + findOrCreateSpecialist({ + agents: { async list() { return duplicateFirst; } }, + }), + /more than one Letta agent is named/, + ); + await assert.rejects( + findOrCreateSpecialist({ agents: { async list() { return []; } } }), + /unsupported result/, + ); + await assert.rejects( + findOrCreateSpecialist({ + agents: { + async list() { + return { + items: [{ id: "cursor-one", name: "near match one" }], + hasNextPage() { + return true; + }, + async getNextPage() { + return { + items: [{ id: "cursor-one", name: "near match two" }], + hasNextPage() { + return true; + }, + async getNextPage() { + throw new Error("must not request a stalled third page"); + }, + }; + }, + }; + }, + }, + }), + /pagination did not advance/, + ); + await assert.rejects( + findOrCreateSpecialist({ + agents: { + async list() { + return { + items: [], + hasNextPage() { + return true; + }, + async getNextPage() { + throw new Error("must not request an unprovable next page"); + }, + }; + }, + }, + }), + /pagination did not advance/, + ); +}); + +test("computer selection is explicit and fails closed when ambiguous", async () => { + const calls = []; + const client = { + computers: { + async list(query) { + calls.push(query); + return { + computers: [ + { + deviceId: "device-observer", + name: "observer-computer", + status: "online", + }, + ], + hasNextPage: false, + }; + }, + }, + }; + assert.deepEqual(await selectComputer(client, {}), { + deviceId: "device-observer", + }); + assert.deepEqual(calls, [{ limit: 100, onlineOnly: true }]); + assert.deepEqual( + await selectComputer(client, { LETTA_COMPUTER_NAME: "chosen-computer" }), + { name: "chosen-computer" }, + ); + assert.equal(calls.length, 1); + await assert.rejects( + selectComputer( + { + computers: { + async list() { + return { + computers: [ + { deviceId: "one", status: "online" }, + { deviceId: "two", status: "online" }, + ], + hasNextPage: false, + }; + }, + }, + }, + {}, + ), + /expected exactly one online Letta computer, found 2/, + ); + await assert.rejects( + selectComputer( + { + computers: { + async list() { + return { + computers: [{ deviceId: " ", status: "online" }], + hasNextPage: false, + }; + }, + }, + }, + {}, + ), + /no stable device id/, + ); }); test("installed SDK finds hidden specialists and translates zero-tool creation", async () => { @@ -199,6 +403,7 @@ test("installed SDK finds hidden specialists and translates zero-tool creation", ]); const observedRequests = []; + let agentCreated = false; const server = http.createServer((request, response) => { const chunks = []; request.on("data", (chunk) => chunks.push(chunk)); @@ -212,9 +417,17 @@ test("installed SDK finds hidden specialists and translates zero-tool creation", }); response.writeHead(200, { "content-type": "application/json" }); if (request.method === "GET") { - response.end("[]"); + const url = new URL(request.url, "http://127.0.0.1"); + response.end( + JSON.stringify( + agentCreated && !url.searchParams.has("after") + ? [specialistState({ id: "agent-sdk-contract" })] + : [], + ), + ); return; } + agentCreated = true; response.end(JSON.stringify({ id: "agent-sdk-contract", name: SPECIALIST_NAME })); }); }); @@ -238,14 +451,21 @@ test("installed SDK finds hidden specialists and translates zero-tool creation", await new Promise((resolve) => server.close(resolve)); } - assert.equal(observedRequests.length, 2); - const [lookupRequest, createRequest] = observedRequests; + assert.equal(observedRequests.length, 4); + const [lookupRequest, createRequest, confirmRequest, paginationRequest] = + observedRequests; assert.equal(lookupRequest.method, "GET"); const lookupUrl = new URL(lookupRequest.url, "http://127.0.0.1"); assert.equal(lookupUrl.pathname, "/v1/agents/"); assert.equal(lookupUrl.searchParams.get("limit"), "100"); assert.equal(lookupUrl.searchParams.get("name"), SPECIALIST_NAME); assert.equal(lookupUrl.searchParams.get("show_hidden_agents"), "true"); + assert.deepEqual(lookupUrl.searchParams.getAll("include"), [ + "agent.blocks", + "agent.sources", + "agent.tags", + "agent.tools", + ]); assert.equal(createRequest.method, "POST"); assert.equal(createRequest.url, "/v1/agents/"); assert.equal(createRequest.body.name, SPECIALIST_NAME); @@ -257,6 +477,11 @@ test("installed SDK finds hidden specialists and translates zero-tool creation", assert.equal(createRequest.body.include_base_tool_rules, false); assert.deepEqual(createRequest.body.tags, ["origin:letta-code"]); assert.match(createRequest.body.system, /no tools, skills, repository access/); + assert.equal(confirmRequest.method, "GET"); + assert.equal(confirmRequest.url, lookupRequest.url); + const paginationUrl = new URL(paginationRequest.url, "http://127.0.0.1"); + assert.equal(paginationRequest.method, "GET"); + assert.equal(paginationUrl.searchParams.get("after"), "agent-sdk-contract"); for (const request of observedRequests) { assert.equal(request.authorization, "Bearer hermetic-contract-key"); } @@ -276,6 +501,18 @@ test("installed SDK carries the zero-authority prompt through Cloud transport", agent_id: agentId, conversation_id: conversationId, }; + const computerEnvironment = { + id: "environment-sdk-transport", + connectedAt: 1, + connectionId, + connectionName, + deviceId: "device-sdk-transport", + firstSeenAt: 1, + lastHeartbeat: 1, + lastSeenAt: 1, + organizationId: "organization-sdk-transport", + podId: "pod-sdk-transport", + }; const restRequests = []; const socketRequests = []; const frames = { control: [], stream: [] }; @@ -299,40 +536,25 @@ test("installed SDK carries the zero-authority prompt through Cloud transport", response.end(JSON.stringify({ id: conversationId, agent_id: agentId })); return; } - if (request.method === "GET" && request.url === "/v1/environments?limit=100") { + if ( + request.method === "GET" && + request.url === "/v1/environments?limit=100&onlineOnly=true" + ) { response.end( JSON.stringify({ - connections: [ - { - id: "environment-decoy", - connectedAt: 1, - connectionId: "connection-decoy", - connectionName: "another-computer", - deviceId: "device-decoy", - firstSeenAt: 1, - lastHeartbeat: 1, - lastSeenAt: 1, - organizationId: "organization-sdk-transport", - podId: "pod-decoy", - }, - { - id: "environment-sdk-transport", - connectedAt: 1, - connectionId, - connectionName, - deviceId: "device-sdk-transport", - firstSeenAt: 1, - lastHeartbeat: 1, - lastSeenAt: 1, - organizationId: "organization-sdk-transport", - podId: "pod-sdk-transport", - }, - ], + connections: [computerEnvironment], hasNextPage: false, }), ); return; } + if ( + request.method === "GET" && + request.url === `/v1/environments/${computerEnvironment.deviceId}` + ) { + response.end(JSON.stringify(computerEnvironment)); + return; + } response.statusCode = 404; response.end(JSON.stringify({ error: `unexpected request: ${request.method} ${request.url}` })); }); @@ -409,11 +631,13 @@ test("installed SDK carries the zero-authority prompt through Cloud transport", apiBaseUrl: `http://127.0.0.1:${address.port}`, apiKey: "hermetic-transport-key", backend: "cloud", - computer: { name: connectionName }, requestTimeoutMs: 5_000, }); + const computer = await selectComputer(client, {}); + assert.deepEqual(computer, { deviceId: computerEnvironment.deviceId }); result = await client.prompt("Interpret this minimized receipt.", agentId, { allowedTools: [], + computer, permissionMode: "strict", skillSources: [], stateless: true, @@ -433,8 +657,9 @@ test("installed SDK carries the zero-authority prompt through Cloud transport", assert.deepEqual( restRequests.map(({ method, url }) => [method, url]), [ + ["GET", "/v1/environments?limit=100&onlineOnly=true"], ["POST", `/v1/conversations/?agent_id=${agentId}`], - ["GET", "/v1/environments?limit=100"], + ["GET", `/v1/environments/${computerEnvironment.deviceId}`], ], ); assert.equal(socketRequests.length, 2); @@ -478,10 +703,10 @@ test("duplicate exact-name specialists fail closed", async () => { const client = { agents: { async list() { - return [ + return agentPage([ { id: "one", name: SPECIALIST_NAME }, { id: "two", name: SPECIALIST_NAME }, - ]; + ]); }, }, }; @@ -527,6 +752,21 @@ test("anomaly interpretation uses retained specialist and a fresh zero-authority class FakeClient { constructor(options) { calls.push(["construct", options]); + this.computers = { + list: async (query) => { + calls.push(["computers", query]); + return { + computers: [ + { + deviceId: "device-observer", + name: "observer-computer", + status: "online", + }, + ], + hasNextPage: false, + }; + }, + }; } async prompt(prompt, agentId, options) { @@ -545,7 +785,7 @@ test("anomaly interpretation uses retained specialist and a fresh zero-authority this.agents = { list: async (query) => { calls.push(["list", query]); - return { items: [{ id: "agent-observer", name: SPECIALIST_NAME }] }; + return agentPage([specialistState()]); }, }; } @@ -574,12 +814,22 @@ test("anomaly interpretation uses retained specialist and a fresh zero-authority ]); assert.deepEqual(calls.find(([kind]) => kind === "list"), [ "list", - { limit: 100, name: SPECIALIST_NAME, show_hidden_agents: true }, + { + include: ["agent.blocks", "agent.sources", "agent.tags", "agent.tools"], + limit: 100, + name: SPECIALIST_NAME, + show_hidden_agents: true, + }, + ]); + assert.deepEqual(calls.find(([kind]) => kind === "computers"), [ + "computers", + { limit: 100, onlineOnly: true }, ]); const promptCall = calls.find(([kind]) => kind === "prompt"); assert.ok(promptCall); assert.equal(promptCall[2], "agent-observer"); assert.deepEqual(promptCall[3].allowedTools, []); + assert.deepEqual(promptCall[3].computer, { deviceId: "device-observer" }); assert.deepEqual(promptCall[3].skillSources, []); assert.deepEqual(promptCall[3].tools, []); assert.deepEqual(promptCall[3].toolset, { base: "none" }); diff --git a/wiki/log/2026-08-12-landing-observer-trust-boundary.md b/wiki/log/2026-08-12-landing-observer-trust-boundary.md index 105cdb52..d20bd900 100644 --- a/wiki/log/2026-08-12-landing-observer-trust-boundary.md +++ b/wiki/log/2026-08-12-landing-observer-trust-boundary.md @@ -39,6 +39,16 @@ it does not add another landing step or widen interpretation authority. API name lookup still reuses the retained identity. Empty memory, disabled MemFS, no server or client tools, no skills, strict permission mode, stateless fresh conversations, and minimized evidence remain binding. +- Exact-name lookup now exhausts every API page and fails closed when pagination is + incomplete or does not advance. Reused specialists and the exact post-create re-read + must prove the expected agent type, name, hidden state, model, system prompt, sole + MemFS-disabled origin tag, empty blocks and sources, and absent tools. A retained + name can no longer conceal authority drift. +- The interpreter no longer relies on ambient SDK computer selection. An explicit + `LETTA_COMPUTER_NAME` goes through named ambiguity/offline checks; otherwise one + complete online listing must contain exactly one computer, and `prompt()` receives + its stable device id. Zero, multiple, malformed, offline, or incompletely enumerated + candidates fail closed before a conversation starts. - A hermetic local HTTP/WebSocket fixture now drives the real pinned `@letta-ai/letta-agent-sdk`. It observes conversation creation, explicit computer resolution, separate control and stream sockets, strict stateless runtime startup, @@ -55,10 +65,12 @@ it does not add another landing step or widen interpretation authority. Focused Python fixtures cover literal and malformed status/diff paths, rename/copy ownership, component-aware ancestor overlap, non-UTF-8 path bytes, pre-landing primary -dirt, completed-landing descendants, and complete Telegram receipt states/message ids. Node fixtures cover -hidden provisioning plus the installed SDK's actual REST/WebSocket boundary and -terminal result. The generated corpus indexes and the exact project landing gate -verify the reconciled candidate. +dirt, completed-landing descendants, and complete Telegram receipt states/message +ids. Node fixtures cover hidden provisioning, exhaustive paginated lookup, every +retained-state authority field, post-create re-read, computer ambiguity and exact +device selection, plus the installed SDK's actual REST/WebSocket boundary and terminal +result. The generated corpus indexes and the exact project landing gate verify the +reconciled candidate. ## Not done @@ -67,6 +79,7 @@ mandatory, modify `tools/task.sh`, or let the observer repair, land, deploy, not schedule, or clean anything it reads. **Defense:** unknown is not absent, and a label is not a receipt. Exact path bytes, -distinct channel states, hidden zero-authority provisioning, and the real pinned -transport each prove only what they can actually reach. Deterministic landing law -remains outside the specialist and outside this observer. +distinct channel states, exhaustive zero-authority identity validation, explicit +computer custody, and the real pinned transport each prove only what they can actually +reach. Deterministic landing law remains outside the specialist and outside this +observer. diff --git a/wiki/log/decisions/2026-08-12.md b/wiki/log/decisions/2026-08-12.md index ebee0641..e53cbabb 100644 --- a/wiki/log/decisions/2026-08-12.md +++ b/wiki/log/decisions/2026-08-12.md @@ -41,11 +41,16 @@ Type: log message id; only `delivered` carries one positive decimal message id. - The retained specialist is hidden from ordinary agent surfaces. The project gate performs a frozen install with pnpm `10.20.0`, then drives the real pinned SDK - through a hermetic HTTP/WebSocket service to prove conversation creation, explicit - computer resolution, dual sockets, strict stateless no-skill startup, synchronization, - empty client-tool authority, interactive-tool exclusion, and terminal completion. - Clean descendants after owned cleanup preserve historical landing proof, while their - benign current-publication lag does not invoke the specialist by itself. + through a hermetic HTTP/WebSocket service to prove conversation creation, exhaustive + specialist lookup, exact retained-state and post-create validation, explicit + computer discovery and device-id resolution, dual sockets, strict stateless no-skill + startup, synchronization, empty client-tool authority, interactive-tool exclusion, + and terminal completion. Exact-name duplicates, incomplete or stalled pagination, + retained-state drift, and ambiguous computer selection fail closed. A configured + `LETTA_COMPUTER_NAME` selects through the SDK's named resolver; otherwise exactly one + completely enumerated online computer is required. Clean descendants after owned + cleanup preserve historical landing proof, while their benign current-publication + lag does not invoke the specialist by itself. ### REJECTED @@ -56,6 +61,10 @@ Type: log fresh and stateless. - **Reuse the specialist's default conversation.** Prior evidence must not leak into the next exact read. +- **Trust a retained specialist because its name still matches, or trust whichever + computer the SDK reaches implicitly.** Retained authority can drift after creation, + and ambient organization machines are not an identity proof. Revalidate the whole + bounded state and bind one explicit computer every time. - **Fetch into, repair, deploy, notify from, schedule through, or clean the observed repository.** Observation has no mutation doorway. - **Poll until the public edge agrees.** Remote publication and edge convergence are diff --git a/wiki/process/repository-skills.md b/wiki/process/repository-skills.md index e01efaca..43b4cac0 100644 --- a/wiki/process/repository-skills.md +++ b/wiki/process/repository-skills.md @@ -40,7 +40,7 @@ Claude mirror is needed. | `design-companion` | `.agents/skills/design-companion/SKILL.md` | Explore and explain unsettled game-design choices in conversation without mutating the repository; hand adopted choices to `design-session`. | | `design-session` | `.agents/skills/design-session/SKILL.md`
`.claude/skills/design-session/SKILL.md` | Capture affirmed design decisions into their owning law/spec pages, decision history, and session trace. | | `playtesting-misaligned` | `.agents/skills/playtesting-misaligned/SKILL.md` | Run evidence-bearing naive and informed playtests against the current player surface and corpus. | -| `observing-misaligned-landings` | `.agents/skills/observing-misaligned-landings/SKILL.md` | Read one exact candidate across isolated remote snapshots, exact dirty-path ownership, public/site and project-operation evidence, classify it without mutation, and optionally ask one retained hidden exact-name, zero-tool Letta specialist in a fresh conversation to interpret only non-benign anomalies. | +| `observing-misaligned-landings` | `.agents/skills/observing-misaligned-landings/SKILL.md` | Read one exact candidate across isolated remote snapshots, exact dirty-path ownership, public/site and project-operation evidence, classify it without mutation, and optionally ask one exhaustively revalidated, hidden, zero-tool Letta specialist on one explicitly selected computer to interpret only non-benign anomalies in a fresh conversation. | | `session-wrap` | `.agents/skills/session-wrap/SKILL.md`
`.claude/skills/session-wrap/SKILL.md` | Finish or preserve owned work, read exact current project state from live authorities, and report one bounded next step without maintaining a parallel checked-in handoff ledger. | | `tick` | `.agents/skills/tick/SKILL.md`
`.claude/skills/tick/SKILL.md` | Invoke the project's bounded stewardship heartbeat: audit one corpus slice, act on one finding, and leave a trace. | @@ -78,10 +78,16 @@ Claude mirror is needed. 7. Optional agent interpretation cannot override deterministic state. It reuses exactly one retained `Misaligned Landing Observer` with empty memory, MemFS and tools disabled, hidden from ordinary agent surfaces, opens a fresh conversation - over minimized evidence for each observation, provisions only when absent, and - fails closed on duplicate exact names. The pinned SDK transport is exercised - end-to-end against a hermetic fake HTTP/WebSocket service after a frozen install; - missing dependencies cannot skip that gate. + over minimized evidence for each observation, and provisions only when absent. + Exact-name lookup exhausts every page; reuse and post-create re-read validate the + exact agent type, identity, hidden/model/system/tag state, empty blocks and sources, + and absent tools. Duplicate names, specialist drift, incomplete pagination, or a + non-advancing cursor fail closed. Computer selection is explicit: a configured + `LETTA_COMPUTER_NAME` uses the SDK's ambiguity- and offline-checking named resolver; + otherwise a complete online listing must contain exactly one computer and the + prompt binds its stable device id. The pinned SDK transport is exercised end-to-end + against a hermetic fake HTTP/WebSocket service after a frozen install; missing + dependencies cannot skip that gate. ## Defense @@ -93,4 +99,6 @@ becoming a second landing authority or a mutation doorway. Exact scoped path comparison, complete channel status/message-id semantics, benign historical-publication handling, hidden provisioning, and real pinned-transport coverage close the remaining places where an observer could claim more certainty or authority than its instruments -earned. +earned. Revalidating retained authority and selecting an explicit computer prevent +ambient organization state from silently changing which identity or machine performs +the supposedly bounded read. diff --git a/wiki/process/workflows.md b/wiki/process/workflows.md index bfab7e3b..6a0908b1 100644 --- a/wiki/process/workflows.md +++ b/wiki/process/workflows.md @@ -292,10 +292,16 @@ Unknown evidence fails closed. A later coherent landing supersedes an older cand rather than retroactively making that older landing inconsistent. Optional Letta interpretation is subordinate and cannot change the state. It reuses one retained exact-name, hidden, empty-memory, zero-tool specialist but opens a fresh conversation -for each observation; duplicate exact names fail closed. Before a landing, NUL-safe -status and diff evidence compares both literal sides of renames and copies: any dirty -primary checkout blocks the existing path until its owner resolves it, and overlap -with the candidate is named exactly rather than guessed unrelated. The candidate delta +for each observation. Exact-name lookup exhausts all pages; each reuse and post-create +read must still prove the exact agent type, name, hidden/model/system/tag state, empty +blocks and sources, and absent tools. Duplicate names, retained-state drift, incomplete +pagination, or a non-advancing cursor fail closed. The prompt names one computer +explicitly: `LETTA_COMPUTER_NAME` uses SDK name resolution, otherwise a complete online +listing must contain exactly one computer and its stable device id is used. Ambiguous, +offline, malformed, or incomplete computer evidence fails closed. Before a landing, +NUL-safe status and diff evidence compares both literal sides of renames and copies: +any dirty primary checkout blocks the existing path until its owner resolves it, and +overlap with the candidate is named exactly rather than guessed unrelated. The candidate delta is explicitly scoped from merge base to candidate while the candidate remains unlanded; once current main contains it, the original delta is unknown rather than mislabeled empty. Missing or different scope cannot prove disjoint ownership. Non-UTF-8 path bytes survive through surrogate escape, every @@ -308,9 +314,10 @@ three non-delivered states carry none. The project gate installs the skill lockfile with its pinned pnpm `10.20.0` before running Node fixtures. Those fixtures drive the real pinned Agent SDK through a hermetic fake HTTP/WebSocket transport and prove conversation creation, explicit -computer resolution, dual sockets, strict stateless startup without skills, -post-start synchronization, empty client-tool authority, interactive-tool exclusion, -and a terminal result. +computer discovery and device-id resolution, exhaustive specialist lookup and state +validation, dual sockets, strict stateless startup without skills, post-start +synchronization, empty client-tool authority, interactive-tool exclusion, and a +terminal result. Do not poll the edge merely because its one public read still serves an older source. Starlight is the documentation renderer. Its sync copies root `DESIGN.md` to @@ -436,5 +443,6 @@ unknowns prevent missing transport, channel, project-status, receipt, candidate- scope, or public evidence from becoming a false green. Completed historical landings and their benign current-publication lag remain local deterministic facts rather than unnecessary agent prompts. Keeping agent interpretation hidden, -zero-authority, transport-tested, optional, and unable to override the four-state -classifier preserves the executable workflow as the only landing authority. +zero-authority, exhaustively revalidated, bound to one explicit computer, +transport-tested, optional, and unable to override the four-state classifier preserves +the executable workflow as the only landing authority.