diff --git a/package.json b/package.json index bbc7b23..a739a66 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@tangle-network/agent-knowledge", - "version": "2.0.0", + "version": "2.0.1", "description": "Source-grounded, eval-gated knowledge growth primitives for agents.", "homepage": "https://github.com/tangle-network/agent-knowledge#readme", "repository": { @@ -68,7 +68,8 @@ "format": "biome format --write src tests" }, "dependencies": { - "@tangle-network/agent-eval": "^0.118.3", + "@tangle-network/agent-eval": "^0.119.1", + "@tangle-network/agent-interface": "^0.26.1", "proper-lockfile": "4.1.2", "zod": "^4.3.6" }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 66f1ff5..58d0ce4 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -13,8 +13,11 @@ importers: .: dependencies: '@tangle-network/agent-eval': - specifier: ^0.118.3 - version: 0.118.3(typescript@5.9.3) + specifier: ^0.119.1 + version: 0.119.1(typescript@5.9.3) + '@tangle-network/agent-interface': + specifier: ^0.26.1 + version: 0.26.1 proper-lockfile: specifier: 4.1.2 version: 4.1.2 @@ -60,8 +63,8 @@ packages: peerDependencies: zod: ^4.0.0 - '@ax-llm/ax@19.0.45': - resolution: {integrity: sha512-zC/OqUutTV0omOuAxBgeCihmxP3e1eUcbA78FCaMF3pPXZ3/FgQDZf0R04LH/lVIxm5BH4/qDdNNYcIVGwii8Q==} + '@ax-llm/ax@23.0.0': + resolution: {integrity: sha512-CWL/vM9RfS0wvVvRbApjYfwhgLa5UOdJno5y2Ime+lWSLYBEwHOhiBJonx08jhh01KoyJYHWS+QcYeInqR7Fsw==} hasBin: true peerDependencies: zod: ^3.24.0 || ^4.0.0 @@ -630,8 +633,8 @@ packages: '@tangle-network/agent-core@0.4.11': resolution: {integrity: sha512-5B1IjrJ8xDR7w8Hv/MSk2ixul6NEJQ5Ftzo+z6l7ipeFpc0yaf1+Kkml4HDUEmGW/rqdrNpBf9/MpT7i/SY0PA==} - '@tangle-network/agent-eval@0.118.3': - resolution: {integrity: sha512-+HQaUHlvT5SH5jCcj7CgOfxnbOv4bqcOucE8r7ZHdo+JgaE9mRbXUTaaYe5wPSqHDUZb5SVtks70mz9olT5Lzg==} + '@tangle-network/agent-eval@0.119.1': + resolution: {integrity: sha512-BkxjITX5FnEjCpZH7iYCmS6puE3iMkSZf5kKZVYQxxQZx/VYj6QBj01vhCdRSKbVSjJkh610A/G0m676yIQ9jw==} engines: {node: '>=20'} hasBin: true @@ -644,6 +647,9 @@ packages: '@tangle-network/agent-interface@0.26.0': resolution: {integrity: sha512-/z4HavFr/9AbaHxi/13bFP9iSUt8oitZbw4wS9g9KAt2TeN2ec5/A2C106udWkKIETZLnu465TfU+K4KDVNkKw==} + '@tangle-network/agent-interface@0.26.1': + resolution: {integrity: sha512-NnQIWtudBHt1P6UXeLMAXUUUxjbYlMLuO3x7yGUGuGWkGcJ0EK0Gpv6aI81g+uqpsV1aHQ4gYfDTCVpdNICUeA==} + '@tangle-network/sandbox@0.9.7': resolution: {integrity: sha512-9pCwJ5MlF7RUpp0AQKQDFyR0yu+E0udEhWkqhrlb/RuoJxlt72zVPuzO4FnMb1MZTkfjStmomC3k5xQyqi1YSA==} peerDependencies: @@ -780,9 +786,6 @@ packages: resolution: {integrity: sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==} engines: {node: ^14.18.0 || >=16.10.0} - dayjs@1.11.20: - resolution: {integrity: sha512-YbwwqR/uYpeoP4pu043q+LTDLFBLApUP6VxRihdfNTqu4ubqMlGDLd6ErXhEgsyvY0K6nCs7nggYumAN+9uEuQ==} - debug@4.4.3: resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} engines: {node: '>=6.0'} @@ -1181,10 +1184,9 @@ snapshots: openapi3-ts: 4.5.0 zod: 4.4.3 - '@ax-llm/ax@19.0.45(zod@4.4.3)': + '@ax-llm/ax@23.0.0(zod@4.4.3)': dependencies: '@opentelemetry/api': 1.9.1 - dayjs: 1.11.20 optionalDependencies: zod: 4.4.3 @@ -1526,13 +1528,13 @@ snapshots: '@tangle-network/agent-interface': 0.26.0 zod: 4.4.3 - '@tangle-network/agent-eval@0.118.3(typescript@5.9.3)': + '@tangle-network/agent-eval@0.119.1(typescript@5.9.3)': dependencies: '@asteasolutions/zod-to-openapi': 8.5.0(zod@4.4.3) - '@ax-llm/ax': 19.0.45(zod@4.4.3) + '@ax-llm/ax': 23.0.0(zod@4.4.3) '@hono/node-server': 2.0.1(hono@4.12.30) '@tangle-network/agent-core': 0.4.11 - '@tangle-network/agent-interface': 0.26.0 + '@tangle-network/agent-interface': 0.26.1 '@tangle-network/tcloud': 0.4.14(typescript@5.9.3)(zod@4.4.3) hono: 4.12.30 zod: 4.4.3 @@ -1558,6 +1560,11 @@ snapshots: '@noble/hashes': 1.8.0 zod: 4.4.3 + '@tangle-network/agent-interface@0.26.1': + dependencies: + '@noble/hashes': 1.8.0 + zod: 4.4.3 + '@tangle-network/sandbox@0.9.7(viem@2.48.8(typescript@5.9.3)(zod@4.4.2))': dependencies: '@tangle-network/agent-core': 0.3.8 @@ -1686,8 +1693,6 @@ snapshots: consola@3.4.2: {} - dayjs@1.11.20: {} - debug@4.4.3: dependencies: ms: 2.1.3 diff --git a/src/agent-candidate.ts b/src/agent-candidate.ts new file mode 100644 index 0000000..171d381 --- /dev/null +++ b/src/agent-candidate.ts @@ -0,0 +1,46 @@ +import { + type AgentCandidateKnowledgeRef, + sha256DigestSchema, +} from '@tangle-network/agent-interface' + +import { + type KnowledgeImprovementCandidateRef, + KnowledgeImprovementCandidateRefSchema, +} from './kb-improvement' + +/** Convert a measured knowledge candidate into the shared review and execution identity. */ +export function toAgentCandidateKnowledgeRef( + candidate: KnowledgeImprovementCandidateRef, +): AgentCandidateKnowledgeRef { + const parsed = KnowledgeImprovementCandidateRefSchema.parse(candidate) + return { + ...parsed, + goalHash: prefixedDigest(parsed.goalHash), + baseHash: prefixedDigest(parsed.baseHash), + candidateHash: prefixedDigest(parsed.candidateHash), + evidenceHash: prefixedDigest(parsed.evidenceHash), + promotionPlanHash: prefixedDigest(parsed.promotionPlanHash), + } +} + +/** Recover agent-knowledge's candidate identity from the shared contract. */ +export function fromAgentCandidateKnowledgeRef( + candidate: AgentCandidateKnowledgeRef, +): KnowledgeImprovementCandidateRef { + return KnowledgeImprovementCandidateRefSchema.parse({ + ...candidate, + goalHash: rawDigest(candidate.goalHash), + baseHash: rawDigest(candidate.baseHash), + candidateHash: rawDigest(candidate.candidateHash), + evidenceHash: rawDigest(candidate.evidenceHash), + promotionPlanHash: rawDigest(candidate.promotionPlanHash), + }) +} + +function prefixedDigest(value: string) { + return sha256DigestSchema.parse(`sha256:${value}`) +} + +function rawDigest(value: string): string { + return sha256DigestSchema.parse(value).slice('sha256:'.length) +} diff --git a/src/index.ts b/src/index.ts index 83fe913..7a99c36 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,5 +1,6 @@ export * from './adapters' export * from './adaptive-driver' +export * from './agent-candidate' export * from './benchmarks/index' export * from './changes' export * from './chunking' diff --git a/tests/agent-candidate.test.ts b/tests/agent-candidate.test.ts new file mode 100644 index 0000000..4d3fb04 --- /dev/null +++ b/tests/agent-candidate.test.ts @@ -0,0 +1,55 @@ +import { + type AgentCandidateKnowledgeRef, + agentCandidateKnowledgeSchema, +} from '@tangle-network/agent-interface' +import { describe, expect, it } from 'vitest' + +import { + fromAgentCandidateKnowledgeRef, + toAgentCandidateKnowledgeRef, +} from '../src/agent-candidate' +import type { KnowledgeImprovementCandidateRef } from '../src/kb-improvement' + +const candidate: KnowledgeImprovementCandidateRef = { + schemaVersion: 1, + kind: 'knowledge-improvement-candidate', + runId: 'knowledge-run', + candidateId: 'candidate-1', + goalHash: '1'.repeat(64), + baseHash: '2'.repeat(64), + candidateHash: '3'.repeat(64), + evidenceHash: '4'.repeat(64), + promotionPlanHash: '5'.repeat(64), +} + +const sharedCandidate: AgentCandidateKnowledgeRef = { + schemaVersion: 1, + kind: 'knowledge-improvement-candidate', + runId: 'knowledge-run', + candidateId: 'candidate-1', + goalHash: `sha256:${'1'.repeat(64)}`, + baseHash: `sha256:${'2'.repeat(64)}`, + candidateHash: `sha256:${'3'.repeat(64)}`, + evidenceHash: `sha256:${'4'.repeat(64)}`, + promotionPlanHash: `sha256:${'5'.repeat(64)}`, +} + +describe('agent candidate knowledge references', () => { + it('converts every hash into an independently validated shared reference', () => { + const shared = toAgentCandidateKnowledgeRef(candidate) + + expect(shared).toEqual(sharedCandidate) + expect(agentCandidateKnowledgeSchema.shape.candidate.parse(shared)).toEqual(sharedCandidate) + }) + + it('recovers the exact knowledge candidate from an independent shared reference', () => { + expect(fromAgentCandidateKnowledgeRef(sharedCandidate)).toEqual(candidate) + }) + + it('rejects malformed shared hashes', () => { + const shared = toAgentCandidateKnowledgeRef(candidate) + expect(() => + fromAgentCandidateKnowledgeRef({ ...shared, candidateHash: 'sha256:bad' }), + ).toThrow() + }) +}) diff --git a/tests/loops/adaptive-ab.test.ts b/tests/loops/adaptive-ab.test.ts index c0254b8..d6ea375 100644 --- a/tests/loops/adaptive-ab.test.ts +++ b/tests/loops/adaptive-ab.test.ts @@ -388,7 +388,7 @@ describe('adaptive A/B (offline, controlled): adaptive escalates only the ambigu // junk adaptive rejected) at strictly fewer LLM calls (2 vs 6 = a 3x cut). expect(adaptiveLlmCalls).toBeLessThan(fullCalls) expect(singleAdmitted).toBeGreaterThan(adaptiveAdmitted) - }) + }, 10_000) }) // ===========================================================================