feat: add 5 research tools and blocks framework setup

- Add @tpmjs/tools-page-brief for URL content extraction
- Add @tpmjs/tools-compare-pages for cross-source validation
- Add @tpmjs/tools-source-credibility for credibility scoring
- Add @tpmjs/tools-claim-checklist for factual claim extraction
- Add @tpmjs/tools-timeline-from-text for timeline generation
- Move createBlogPost to packages/tools/official/
- Add blocks.yml for Blocks framework validation
- Update pnpm-workspace.yaml to include official tools
This commit is contained in:
Ajax Davis 2025-12-31 19:47:02 +10:00
parent 7edd4f43b2
commit aaf3cdd424
34 changed files with 2458 additions and 61 deletions

View file

@ -0,0 +1,21 @@
---
"@tpmjs/tools-page-brief": minor
"@tpmjs/tools-compare-pages": minor
"@tpmjs/tools-source-credibility": minor
"@tpmjs/tools-claim-checklist": minor
"@tpmjs/tools-timeline-from-text": minor
---
Add 5 new research tools for AI-powered content analysis
New tools using AI SDK v6 beta (tool() + jsonSchema() pattern):
- **@tpmjs/tools-page-brief**: Fetch URL and extract summary with key points and claims needing citations
- **@tpmjs/tools-compare-pages**: Compare two URLs for agreements, conflicts, and unique points
- **@tpmjs/tools-source-credibility**: Calculate heuristic credibility score based on domain signals
- **@tpmjs/tools-claim-checklist**: Extract checkable factual claims from text
- **@tpmjs/tools-timeline-from-text**: Extract dated events and return normalized timeline
All tools are stub implementations ready for full logic implementation.
Also moved @tpmjs/createblogpost to packages/tools/official/ directory and set up blocks.yml for Blocks framework validation.

View file

@ -130,6 +130,27 @@ GET /api/users/[username]/collections - Get user's public collections
---
## Benchmark Comparison Page
### Overview
- [ ] Create `/benchmarks` page to compare tool performance
- [ ] Display execution time comparisons across similar tools
- [ ] Show token usage efficiency metrics
- [ ] Compare success rates and reliability
### Features
- [ ] Side-by-side tool comparison UI
- [ ] Historical performance trends (charts)
- [ ] Filter by category to compare relevant tools
- [ ] Export benchmark data as JSON/CSV
### Data Collection
- [ ] Track execution metrics from playground simulations
- [ ] Aggregate anonymous performance data
- [ ] Calculate percentile rankings within categories
---
## Notes
- Consider rate limiting on ratings to prevent abuse

View file

@ -40,6 +40,8 @@
},
"devDependencies": {
"@biomejs/biome": "^1.9.4",
"@blocksai/cli": "^0.2.1",
"@blocksai/validators": "^1.1.1",
"@changesets/cli": "^2.27.10",
"@total-typescript/ts-reset": "^0.6.1",
"@types/node": "^22.10.2",

3
packages/tools/official/.gitignore vendored Normal file
View file

@ -0,0 +1,3 @@
# Blocks CLI runtime files
.blocks/
.env

View file

@ -0,0 +1,168 @@
# Blocks Domain Validator Bug Report
## Status: FIXED
The fix has been applied to `/Users/ajaxdavis/repos/blocks/packages/ai/src/provider.ts`.
---
## Summary
The domain validator in `@blocksai/validators` was failing with an OpenAI structured output schema error when validating any block. The schema and shape.ts validators worked correctly.
## Error Message
```
⚠ [domain] AI validation failed: Invalid schema for response_format 'response':
In context=('properties', 'issues', 'items'), 'required' is required to be supplied
and to be an array including every key in properties. Missing 'file'.
```
## Environment
- `@blocksai/cli`: latest (installed via npx)
- `@blocksai/validators`: latest
- OpenAI API: Using `OPENAI_API_KEY` from .env
- Node.js: v22.x
- OS: macOS
## Reproduction Steps
1. Create a valid `blocks.yml` with any block definition
2. Create the corresponding TypeScript file with proper exports
3. Run `npx blocks run --all`
## Example blocks.yml
```yaml
name: "tpmjs-official-tools"
root: "."
philosophy:
- "Tools must be pure functions with no side effects"
domain:
entities:
url:
fields: [href, domain, protocol, path]
signals:
credibility:
description: "Trustworthiness of a source"
measures:
valid_output:
constraints:
- "Must return structured object matching interface"
blocks:
domain_rules:
- id: pure_function
description: "Tool must be deterministic with no side effects"
research.pageBrief:
description: "Fetch URL and extract content"
path: "page-brief"
inputs:
- name: url
type: string
outputs:
- name: brief
type: PageBrief
measures: [valid_output]
validators:
- schema
- shape.ts
- domain
```
## Observed Behavior
```
📦 Validating: research.pageBrief
✓ schema ok
✓ shape.ts ok
- Running domain...
⚠ [domain] AI validation failed: Invalid schema for response_format 'response':
In context=('properties', 'issues', 'items'), 'required' is required to be
supplied and to be an array including every key in properties. Missing 'file'.
⚠️ Block "research.pageBrief" has warnings
```
## Root Cause Analysis
The error message indicates that the domain validator is constructing an OpenAI structured output request with a JSON schema that's missing required fields. Specifically:
1. OpenAI's structured output feature requires that all properties in an object schema must be listed in the `required` array
2. The domain validator's internal response schema has a `file` property in the `issues.items` object
3. This `file` property is not included in the corresponding `required` array
This is an internal schema construction issue within the domain validator, not related to user-provided blocks.yml configuration.
## Expected Behavior
The domain validator should:
1. Construct a valid JSON schema for OpenAI's structured output API
2. Ensure all properties are listed in `required` arrays
3. Successfully analyze the block against the domain rules and philosophy
## Workaround
Currently, the schema and shape.ts validators work correctly. The domain validator can be skipped by removing `domain` from the validators list in blocks.yml:
```yaml
validators:
- schema
- shape.ts
# - domain # Disabled due to bug
```
## Fix Applied
**File:** `/Users/ajaxdavis/repos/blocks/packages/ai/src/provider.ts` (lines 163-173)
**Before (broken):**
```typescript
const schema = z.object({
isValid: z.boolean(),
issues: z.array(
z.object({
message: z.string(),
severity: z.enum(["error", "warning"]),
file: z.string().optional(), // ← Problem: .optional() excludes from required
})
),
summary: z.string().optional().describe("..."),
});
```
**After (fixed):**
```typescript
const schema = z.object({
isValid: z.boolean(),
issues: z.array(
z.object({
message: z.string().describe("Description of the issue found"),
severity: z.enum(["error", "warning"]).describe("Severity of the issue"),
file: z.string().describe("File path where the issue was found, or empty string if not file-specific"),
})
),
summary: z.string().describe("Brief summary of why the block passed or failed validation"),
});
```
**Root Cause:** OpenAI's structured output requires ALL properties to be in the JSON schema's `required` array. When Zod converts `.optional()` to JSON schema, it omits that property from `required`, causing OpenAI to reject the schema.
**Solution:** Remove `.optional()` and `.default()` modifiers. Make all fields required strings. The AI will return an empty string for file-agnostic issues.
## Impact
- **Severity**: Medium - domain validation is non-functional
- **Affected**: All blocks using the domain validator
- **Workaround available**: Yes - disable domain validator
## Additional Context
This was tested with 6 different blocks, all showing the same error. The error is consistent and reproducible regardless of block configuration.

View file

@ -0,0 +1,132 @@
name: "tpmjs-official-tools"
root: "."
philosophy:
- "Tools must be pure functions with no side effects"
- "Each tool solves one specific problem well"
- "Tools are publishable to npm as @tpmjs/* packages"
- "Use AI SDK v6 tool() + jsonSchema() pattern"
domain:
entities:
url:
fields: [href, domain, protocol, path]
text:
fields: [content, sentences, wordCount]
claim:
fields: [statement, needsCitation, evidence]
timeline_event:
fields: [date, description, confidence]
signals:
credibility:
description: "Trustworthiness of a source"
extraction_hint: "Look for author, date, citations, HTTPS"
readability:
description: "How easy content is to understand"
extraction_hint: "Check sentence length, jargon, structure"
measures:
valid_output:
constraints:
- "Must return structured object matching interface"
- "Must not throw unhandled errors"
npm_publishable:
constraints:
- "Must have valid package.json with tpmjs field"
- "Must export tool as named and default export"
blocks:
domain_rules:
- id: pure_function
description: "Tool must be deterministic with no side effects"
- id: ai_sdk_pattern
description: "Must use AI SDK v6 tool() + jsonSchema()"
- id: npm_ready
description: "Must be publishable to npm with complete metadata"
adapter.createBlogPost:
description: "Creates structured blog posts with frontmatter and metadata"
path: "createBlogPost"
inputs:
- name: title
type: string
- name: author
type: string
- name: content
type: string
- name: tags
type: string[]
optional: true
- name: format
type: "'markdown' | 'mdx'"
optional: true
outputs:
- name: blogPost
type: BlogPost
measures: [valid_output, npm_publishable]
research.pageBrief:
description: "Fetch URL, extract main content, return summary with key points and claims needing citations"
path: "page-brief"
inputs:
- name: url
type: string
outputs:
- name: brief
type: PageBrief
measures: [valid_output]
research.comparePages:
description: "Compare two URLs for agreements, conflicts, and unique points"
path: "compare-pages"
inputs:
- name: urlA
type: string
- name: urlB
type: string
outputs:
- name: comparison
type: PageComparison
measures: [valid_output]
research.sourceCredibility:
description: "Heuristic credibility score based on domain signals, author presence, citations"
path: "source-credibility"
inputs:
- name: url
type: string
- name: html
type: string
optional: true
outputs:
- name: credibility
type: CredibilityScore
measures: [valid_output]
research.claimChecklist:
description: "Extract checkable factual claims from text, mark what needs citations"
path: "claim-checklist"
inputs:
- name: text
type: string
outputs:
- name: checklist
type: ClaimChecklist
measures: [valid_output]
research.timelineFromText:
description: "Extract dated events from text, return normalized timeline with confidence"
path: "timeline-from-text"
inputs:
- name: text
type: string
outputs:
- name: timeline
type: Timeline
measures: [valid_output]
validators:
- schema
- shape.ts
- domain

View file

@ -0,0 +1,61 @@
{
"name": "@tpmjs/tools-claim-checklist",
"version": "0.1.0",
"description": "Extract checkable factual claims from text and identify what needs citations",
"type": "module",
"keywords": ["tpmjs", "research", "ai", "claims", "fact-checking", "citations"],
"exports": {
".": {
"types": "./dist/index.d.ts",
"default": "./dist/index.js"
}
},
"files": ["dist"],
"scripts": {
"build": "tsup",
"dev": "tsup --watch",
"type-check": "tsc --noEmit",
"clean": "rm -rf dist .turbo"
},
"devDependencies": {
"@tpmjs/tsconfig": "workspace:*",
"tsup": "^8.3.5",
"typescript": "^5.9.3"
},
"publishConfig": {
"access": "public"
},
"repository": {
"type": "git",
"url": "https://github.com/anthropics/tpmjs.git",
"directory": "packages/tools/official/claim-checklist"
},
"homepage": "https://tpmjs.com",
"license": "MIT",
"tpmjs": {
"category": "research",
"frameworks": ["vercel-ai"],
"tools": [
{
"name": "claimChecklistTool",
"description": "Extract checkable factual claims from text and mark what needs citations",
"parameters": [
{
"name": "text",
"type": "string",
"description": "The text to analyze for factual claims",
"required": true
}
],
"returns": {
"type": "ClaimChecklist",
"description": "Object with claims array and summary statistics"
}
}
]
},
"dependencies": {
"ai": "6.0.0-beta.124",
"sbd": "^1.0.19"
}
}

View file

@ -0,0 +1,56 @@
import { jsonSchema, tool } from 'ai';
export interface ClaimChecklist {
originalText: string;
claims: Array<{
claim: string;
needsCitation: boolean;
evidenceType: 'statistic' | 'fact' | 'quote' | 'opinion' | 'common-knowledge';
suggestedEvidence: string;
sentenceIndex: number;
}>;
summary: {
totalClaims: number;
needingCitation: number;
verified: number;
};
}
type ClaimChecklistInput = {
text: string;
};
export const claimChecklistTool = tool({
description:
'Extract checkable factual claims from text, mark what needs citations, and suggest what evidence would support each claim',
inputSchema: jsonSchema<ClaimChecklistInput>({
type: 'object',
properties: {
text: {
type: 'string',
description: 'The text to analyze for factual claims',
},
},
required: ['text'],
additionalProperties: false,
}),
async execute({ text }): Promise<ClaimChecklist> {
// TODO: Implement with:
// 1. Split text into sentences with sbd
// 2. Rule-based claim extraction (numbers, dates, proper nouns)
// 3. Classify claim types
// 4. Determine if citation needed based on type
return {
originalText: text,
claims: [],
summary: {
totalClaims: 0,
needingCitation: 0,
verified: 0,
},
};
},
});
export default claimChecklistTool;

View file

@ -0,0 +1,67 @@
{
"name": "@tpmjs/tools-compare-pages",
"version": "0.1.0",
"description": "Compare two URLs for agreements, conflicts, and unique points for cross-source validation",
"type": "module",
"keywords": ["tpmjs", "research", "ai", "comparison", "fact-checking"],
"exports": {
".": {
"types": "./dist/index.d.ts",
"default": "./dist/index.js"
}
},
"files": ["dist"],
"scripts": {
"build": "tsup",
"dev": "tsup --watch",
"type-check": "tsc --noEmit",
"clean": "rm -rf dist .turbo"
},
"devDependencies": {
"@tpmjs/tsconfig": "workspace:*",
"tsup": "^8.3.5",
"typescript": "^5.9.3"
},
"publishConfig": {
"access": "public"
},
"repository": {
"type": "git",
"url": "https://github.com/anthropics/tpmjs.git",
"directory": "packages/tools/official/compare-pages"
},
"homepage": "https://tpmjs.com",
"license": "MIT",
"tpmjs": {
"category": "research",
"frameworks": ["vercel-ai"],
"tools": [
{
"name": "comparePagesTool",
"description": "Compare two URLs for agreements, conflicts, and unique points",
"parameters": [
{
"name": "urlA",
"type": "string",
"description": "First URL to compare",
"required": true
},
{
"name": "urlB",
"type": "string",
"description": "Second URL to compare",
"required": true
}
],
"returns": {
"type": "PageComparison",
"description": "Object with agreements, conflicts, uniqueToA, and uniqueToB arrays"
}
}
]
},
"dependencies": {
"ai": "6.0.0-beta.124",
"natural": "^8.0.0"
}
}

View file

@ -0,0 +1,57 @@
import { jsonSchema, tool } from 'ai';
export interface PageComparison {
urlA: string;
urlB: string;
agreements: string[];
conflicts: Array<{
topic: string;
pageAPosition: string;
pageBPosition: string;
}>;
uniqueToA: string[];
uniqueToB: string[];
}
type ComparePagesInput = {
urlA: string;
urlB: string;
};
export const comparePagesTool = tool({
description:
'Compare two URLs for agreements, conflicts, and unique points to help with cross-source validation',
inputSchema: jsonSchema<ComparePagesInput>({
type: 'object',
properties: {
urlA: {
type: 'string',
description: 'First URL to compare',
},
urlB: {
type: 'string',
description: 'Second URL to compare',
},
},
required: ['urlA', 'urlB'],
additionalProperties: false,
}),
async execute({ urlA, urlB }): Promise<PageComparison> {
// TODO: Implement with:
// 1. Call pageBriefTool on both URLs
// 2. Extract key points from both
// 3. Use TF-IDF (natural) for semantic matching
// 4. Identify agreements, conflicts, and unique points
return {
urlA,
urlB,
agreements: [],
conflicts: [],
uniqueToA: [],
uniqueToB: [],
};
},
});
export default comparePagesTool;

View file

@ -0,0 +1,11 @@
{
"extends": "@tpmjs/tsconfig/base.json",
"compilerOptions": {
"outDir": "dist",
"rootDir": "src",
"incremental": false,
"composite": false
},
"include": ["src"],
"exclude": ["node_modules", "dist"]
}

View file

@ -0,0 +1,10 @@
import { defineConfig } from 'tsup';
export default defineConfig({
entry: ['src/index.ts'],
format: ['esm'],
dts: true,
clean: true,
treeshake: true,
splitting: false,
});

View file

@ -28,7 +28,7 @@
"repository": {
"type": "git",
"url": "https://github.com/ajaxdavis/tpmjs.git",
"directory": "packages/tools/createBlogPost"
"directory": "packages/tools/official/createBlogPost"
},
"homepage": "https://tpmjs.com",
"license": "MIT",

View file

@ -0,0 +1,11 @@
{
"extends": "@tpmjs/tsconfig/base.json",
"compilerOptions": {
"outDir": "dist",
"rootDir": "src",
"incremental": false,
"composite": false
},
"include": ["src"],
"exclude": ["node_modules", "dist"]
}

View file

@ -0,0 +1,10 @@
import { defineConfig } from 'tsup';
export default defineConfig({
entry: ['src/index.ts'],
format: ['esm'],
dts: true,
clean: true,
treeshake: true,
splitting: false,
});

View file

@ -0,0 +1,63 @@
{
"name": "@tpmjs/tools-page-brief",
"version": "0.1.0",
"description": "Fetch a URL, extract main content, and return a brief with summary, key points, and claims needing citations",
"type": "module",
"keywords": ["tpmjs", "research", "ai", "readability", "web-scraping"],
"exports": {
".": {
"types": "./dist/index.d.ts",
"default": "./dist/index.js"
}
},
"files": ["dist"],
"scripts": {
"build": "tsup",
"dev": "tsup --watch",
"type-check": "tsc --noEmit",
"clean": "rm -rf dist .turbo"
},
"devDependencies": {
"@tpmjs/tsconfig": "workspace:*",
"tsup": "^8.3.5",
"typescript": "^5.9.3"
},
"publishConfig": {
"access": "public"
},
"repository": {
"type": "git",
"url": "https://github.com/anthropics/tpmjs.git",
"directory": "packages/tools/official/page-brief"
},
"homepage": "https://tpmjs.com",
"license": "MIT",
"tpmjs": {
"category": "research",
"frameworks": ["vercel-ai"],
"tools": [
{
"name": "pageBriefTool",
"description": "Fetch a URL, extract main content, and return a brief with summary, key points, and claims needing citations",
"parameters": [
{
"name": "url",
"type": "string",
"description": "The URL to fetch and analyze",
"required": true
}
],
"returns": {
"type": "PageBrief",
"description": "Object with url, title, summary, keyPoints array, and claimsNeedingCitation array"
}
}
]
},
"dependencies": {
"ai": "6.0.0-beta.124",
"@mozilla/readability": "^0.5.0",
"jsdom": "^26.0.0",
"sbd": "^1.0.19"
}
}

View file

@ -0,0 +1,50 @@
import { jsonSchema, tool } from 'ai';
export interface PageBrief {
url: string;
title: string;
summary: string;
keyPoints: string[];
claimsNeedingCitation: Array<{
claim: string;
suggestedEvidence: string;
}>;
}
type PageBriefInput = {
url: string;
};
export const pageBriefTool = tool({
description:
'Fetch a URL, extract main content using readability, and return a brief with summary, key points, and claims that need citations',
inputSchema: jsonSchema<PageBriefInput>({
type: 'object',
properties: {
url: {
type: 'string',
description: 'The URL to fetch and analyze',
},
},
required: ['url'],
additionalProperties: false,
}),
async execute({ url }): Promise<PageBrief> {
// TODO: Implement with fetch + @mozilla/readability + jsdom + sbd
// 1. Fetch the URL
// 2. Parse HTML with jsdom
// 3. Extract main content with Readability
// 4. Split into sentences with sbd
// 5. Identify claims that need citations
return {
url,
title: 'Not implemented',
summary: 'This is a stub implementation. Real implementation will use @mozilla/readability.',
keyPoints: [],
claimsNeedingCitation: [],
};
},
});
export default pageBriefTool;

View file

@ -0,0 +1,11 @@
{
"extends": "@tpmjs/tsconfig/base.json",
"compilerOptions": {
"outDir": "dist",
"rootDir": "src",
"incremental": false,
"composite": false
},
"include": ["src"],
"exclude": ["node_modules", "dist"]
}

View file

@ -0,0 +1,10 @@
import { defineConfig } from 'tsup';
export default defineConfig({
entry: ['src/index.ts'],
format: ['esm'],
dts: true,
clean: true,
treeshake: true,
splitting: false,
});

View file

@ -0,0 +1,68 @@
{
"name": "@tpmjs/tools-source-credibility",
"version": "0.1.0",
"description": "Calculate heuristic credibility score based on domain signals, author presence, and citations",
"type": "module",
"keywords": ["tpmjs", "research", "ai", "credibility", "fact-checking"],
"exports": {
".": {
"types": "./dist/index.d.ts",
"default": "./dist/index.js"
}
},
"files": ["dist"],
"scripts": {
"build": "tsup",
"dev": "tsup --watch",
"type-check": "tsc --noEmit",
"clean": "rm -rf dist .turbo"
},
"devDependencies": {
"@tpmjs/tsconfig": "workspace:*",
"tsup": "^8.3.5",
"typescript": "^5.9.3"
},
"publishConfig": {
"access": "public"
},
"repository": {
"type": "git",
"url": "https://github.com/anthropics/tpmjs.git",
"directory": "packages/tools/official/source-credibility"
},
"homepage": "https://tpmjs.com",
"license": "MIT",
"tpmjs": {
"category": "research",
"frameworks": ["vercel-ai"],
"tools": [
{
"name": "sourceCredibilityTool",
"description": "Calculate heuristic credibility score based on domain signals",
"parameters": [
{
"name": "url",
"type": "string",
"description": "URL to analyze for credibility",
"required": true
},
{
"name": "html",
"type": "string",
"description": "Optional pre-fetched HTML content",
"required": false
}
],
"returns": {
"type": "CredibilityScore",
"description": "Object with score (0-1), signals breakdown, and detailed analysis"
}
}
]
},
"dependencies": {
"ai": "6.0.0-beta.124",
"tldts": "^6.1.0",
"cheerio": "^1.0.0"
}
}

View file

@ -0,0 +1,75 @@
import { jsonSchema, tool } from 'ai';
export interface CredibilityScore {
url: string;
score: number; // 0.0 to 1.0
signals: {
hasHttps: boolean;
hasAuthor: boolean;
hasPublishDate: boolean;
hasCitations: boolean;
domainAge?: string;
isKnownSource: boolean;
};
breakdown: Array<{
signal: string;
weight: number;
present: boolean;
}>;
}
type SourceCredibilityInput = {
url: string;
html?: string;
};
export const sourceCredibilityTool = tool({
description:
'Calculate a heuristic credibility score based on domain signals, author presence, date, citations density, and HTTPS',
inputSchema: jsonSchema<SourceCredibilityInput>({
type: 'object',
properties: {
url: {
type: 'string',
description: 'URL to analyze for credibility',
},
html: {
type: 'string',
description: 'Optional: pre-fetched HTML content to analyze',
},
},
required: ['url'],
additionalProperties: false,
}),
async execute({ url, html: _html }): Promise<CredibilityScore> {
// TODO: Implement with:
// 1. Parse URL with tldts for domain info
// 2. If no HTML provided, fetch the page
// 3. Parse HTML with cheerio for meta signals
// 4. Check for author, date, citations
// 5. Calculate weighted score
const isHttps = url.startsWith('https://');
return {
url,
score: 0.5, // Stub score
signals: {
hasHttps: isHttps,
hasAuthor: false,
hasPublishDate: false,
hasCitations: false,
isKnownSource: false,
},
breakdown: [
{ signal: 'https', weight: 0.1, present: isHttps },
{ signal: 'author', weight: 0.2, present: false },
{ signal: 'publishDate', weight: 0.2, present: false },
{ signal: 'citations', weight: 0.3, present: false },
{ signal: 'knownSource', weight: 0.2, present: false },
],
};
},
});
export default sourceCredibilityTool;

View file

@ -0,0 +1,11 @@
{
"extends": "@tpmjs/tsconfig/base.json",
"compilerOptions": {
"outDir": "dist",
"rootDir": "src",
"incremental": false,
"composite": false
},
"include": ["src"],
"exclude": ["node_modules", "dist"]
}

View file

@ -0,0 +1,10 @@
import { defineConfig } from 'tsup';
export default defineConfig({
entry: ['src/index.ts'],
format: ['esm'],
dts: true,
clean: true,
treeshake: true,
splitting: false,
});

View file

@ -0,0 +1,62 @@
{
"name": "@tpmjs/tools-timeline-from-text",
"version": "0.1.0",
"description": "Extract dated events from text and return a normalized timeline with confidence scores",
"type": "module",
"keywords": ["tpmjs", "research", "ai", "timeline", "dates", "events"],
"exports": {
".": {
"types": "./dist/index.d.ts",
"default": "./dist/index.js"
}
},
"files": ["dist"],
"scripts": {
"build": "tsup",
"dev": "tsup --watch",
"type-check": "tsc --noEmit",
"clean": "rm -rf dist .turbo"
},
"devDependencies": {
"@tpmjs/tsconfig": "workspace:*",
"tsup": "^8.3.5",
"typescript": "^5.9.3"
},
"publishConfig": {
"access": "public"
},
"repository": {
"type": "git",
"url": "https://github.com/anthropics/tpmjs.git",
"directory": "packages/tools/official/timeline-from-text"
},
"homepage": "https://tpmjs.com",
"license": "MIT",
"tpmjs": {
"category": "research",
"frameworks": ["vercel-ai"],
"tools": [
{
"name": "timelineFromTextTool",
"description": "Extract dated events from text and return normalized timeline",
"parameters": [
{
"name": "text",
"type": "string",
"description": "The text to extract timeline events from",
"required": true
}
],
"returns": {
"type": "Timeline",
"description": "Object with events array and optional date range"
}
}
]
},
"dependencies": {
"ai": "6.0.0-beta.124",
"chrono-node": "^2.7.0",
"sbd": "^1.0.19"
}
}

View file

@ -0,0 +1,50 @@
import { jsonSchema, tool } from 'ai';
export interface Timeline {
originalText: string;
events: Array<{
date: string; // ISO format
description: string;
confidence: number; // 0.0 to 1.0
originalMention: string;
}>;
dateRange?: {
earliest: string;
latest: string;
};
}
type TimelineFromTextInput = {
text: string;
};
export const timelineFromTextTool = tool({
description:
'Extract dated events from text and return a normalized timeline with confidence scores per event',
inputSchema: jsonSchema<TimelineFromTextInput>({
type: 'object',
properties: {
text: {
type: 'string',
description: 'The text to extract timeline events from',
},
},
required: ['text'],
additionalProperties: false,
}),
async execute({ text }): Promise<Timeline> {
// TODO: Implement with:
// 1. Use chrono-node to parse dates from text
// 2. Extract sentence context around each date
// 3. Normalize dates to ISO format
// 4. Assign confidence based on date specificity
return {
originalText: text,
events: [],
dateRange: undefined,
};
},
});
export default timelineFromTextTool;

View file

@ -0,0 +1,11 @@
{
"extends": "@tpmjs/tsconfig/base.json",
"compilerOptions": {
"outDir": "dist",
"rootDir": "src",
"incremental": false,
"composite": false
},
"include": ["src"],
"exclude": ["node_modules", "dist"]
}

View file

@ -0,0 +1,10 @@
import { defineConfig } from 'tsup';
export default defineConfig({
entry: ['src/index.ts'],
format: ['esm'],
dts: true,
clean: true,
treeshake: true,
splitting: false,
});

1455
pnpm-lock.yaml generated

File diff suppressed because it is too large Load diff

View file

@ -5,3 +5,4 @@ packages:
- 'packages/config/tailwind'
- 'packages/config/tsconfig'
- 'packages/tools/*'
- 'packages/tools/official/*'