// Real bash tools and extension commands retain the nearest-first grant ancestry (D47, D63). import { BACKGROUND_CONTEXT as context } from '@earendil-works/chord/context'; import { createModels } from '@earendil-works/pi-ai/models'; import { fauxProvider, fauxAssistantMessage, fauxToolCall } from '@earendil-works/pi-ai/providers/faux'; import { Harness, createRegistry, defineDoc, defineExtension, defineTask } from '@earendil-works/pi-durable'; import { CodingTools } from '@earendil-works/pi-durable/tools'; import { environment, conversationChains } from './environment'; import { exec, fs } from './extensions'; import { openStorage } from './sqlite'; const assert = (condition: any, message: string) => { if (!condition) throw new Error(message); }; const equal = (a: any, b: any) => assert(JSON.stringify(a) === JSON.stringify(b), 'Wrong conversation chain'); const Started = defineDoc<{ by?: number }>({ kind: 'board.started', version: 1, checkpointWhen: () => false, scope: 'conversation', history: 'latest', fork: 'initial', initial: () => ({}) }); const Anchor = defineTask({ name: 'chain.anchor', version: 1, initial: () => ({ phase: 'done' }), phases: { done: (_task: any, runtime: any, ctx: any) => runtime.commit(() => ({ status: 'terminal', outcome: { status: 'completed', result: null } }), ctx) } }); const models = createModels(), faux = fauxProvider({ tokensPerSecond: 100000 }); models.setProvider(faux.provider); const registry = createRegistry(); registry.install(CodingTools); registry.install(defineExtension({ name: 'chain', tasks: [Anchor] })); const harness = await Harness.open(await openStorage(globalThis.storagePath), { models, registry, env: async (target: any) => environment('/', await chain(target.conversationId)) }, context); const chain = conversationChains(harness, context); const agent = { model: { provider: 'faux', modelId: 'faux-1' }, extensions: [CodingTools], cwd: '/' }; const root = await harness.root(context, { agent }); const owned = async (parent: any) => parent.commit(async (tx: any) => { const taskId = await tx.createTask(Anchor, null, { ownership: { kind: 'conversation' } }); return tx.createConversation({ ownership: { kind: 'task', taskId } }); }, context); let records: any; if (!globalThis.chainReady) { const parentRecord = await owned(root); const parent = await harness.conversation(parentRecord.id, context); const child = await owned(parent); // Starter ancestry can repeat an owner ancestor, but the chain lists it once. await harness.commit(async (tx: any) => { (await tx.doc(Started, child.id)).by = root.id; }, context); const entry = await parent.commit((tx: any) => tx.appendEntry(parent.id, { kind: 'chain.origin', data: {} }), context); const fork = await parent.commit((tx: any) => tx.forkConversation(parent.id, entry.id, { ownership: { kind: 'ownerless' } }), context); const unrelated = await harness.commit((tx: any) => tx.createConversation({ ownership: { kind: 'ownerless' } }), context); const branch = await harness.commit(async (tx: any) => { const record = await tx.createConversation({ ownership: { kind: 'ownerless' } }); (await tx.doc(Started, record.id)).by = parent.id; return record; }, context); const ownedFork = await root.commit(async (tx: any) => { const taskId = await tx.createTask(Anchor, null, { ownership: { kind: 'conversation' } }); (await tx.doc(Started, fork.id)).by = unrelated.id; return tx.forkConversation(parent.id, fork.parent.at, { ownership: { kind: 'task', taskId } }); }, context); records = { parent: parentRecord, child, fork, branch, unrelated, ownedFork }; await fs.write('/chain-ids', JSON.stringify(records)); await harness.close(context); } else { records = JSON.parse(await fs.read('/chain-ids')); const { parent, child, fork, branch, unrelated, ownedFork } = records; harness.resume(); try { for (const record of [child, fork, branch, unrelated]) { const conversation = await harness.conversation(record.id, context); await conversation.configure(agent, context); faux.setResponses([ fauxAssistantMessage([fauxToolCall('bash', { command: `curl http://host.sausage/whoami` }, { id: `identity-${record.id}` })], { stopReason: 'toolUse' }), fauxAssistantMessage([fauxToolCall('bash', { command: `curl -i http://127.0.0.1:${globalThis.chainPort}/token` }, { id: `fetch-${record.id}` })], { stopReason: 'toolUse' }), fauxAssistantMessage('done'), ]); await (await conversation.submit({ type: 'input', content: 'Check inherited access' }, context)).wait(context); const entries = (await conversation.context(context)).entries; const tools = entries.filter((entry: any) => entry.kind === 'pi.tool-result'); const output = (entry: any) => entry.model.flatMap((message: any) => message.content ?? []).filter((block: any) => block.type === 'text').map((block: any) => block.text).join(''); const identity = JSON.parse(output(tools[0])); equal(identity.conversations, record.id === unrelated.id ? [String(record.id)] : [String(record.id), String(parent.id), String(root.id), ...(record.id === fork.id ? [String(unrelated.id)] : [])]); const response = output(tools[1]); assert(response.includes(record.id === unrelated.id ? '403' : 'accepted'), 'Wrong inherited authorization'); } equal(await chain(ownedFork.id), [String(ownedFork.id), String(root.id)]); assert(await harness.snapshot(Started, unrelated.id, context) === undefined, 'Missing starter must remain absent'); assert(chain(child.id) === chain(child.id), 'The chain must use its cached promise'); const denied = await exec(['curl', '-i', `http://127.0.0.1:${globalThis.chainPort}/unrelated`], { cwd: '/', conversations: await chain(child.id) }); assert(denied.stdout.includes('403'), 'An unrelated grant must not cover the child'); const delegated = await exec(['curl', 'http://host.sausage/whoami'], { cwd: '/', conversations: await chain(child.id) }); assert(delegated.code === 0, 'Extension exec failed'); equal(JSON.parse(delegated.stdout).conversations, [String(child.id), String(parent.id), String(root.id)]); const cleared = await exec(['curl', 'http://host.sausage/whoami'], { cwd: '/', conversations: [] }); assert(cleared.code === 0, 'Explicit empty chain failed'); equal(JSON.parse(cleared.stdout).conversations, []); } finally { await harness.close(context); } }