Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 3 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ Sophia Agent is an autonomous workflow agent that monitors, logs, and notifies a

## Features

- **Watches** for new wiki creations or edits by Sophia on iq.wiki
- **Checks** for new wiki creations or edits by Sophia on iq.wiki
- **Logs** activities to the $SOPHIA agent on IQAI ATP
- **Notifies** a Telegram channel about new activities
- **Scheduled** to run at configurable intervals (via cron)
Expand Down Expand Up @@ -67,9 +67,8 @@ pnpm run start

## Project Structure

- `src/agents/` — Agent definitions (Watcher, Logger, Notifier, SophiaAgent)
- `src/utils/` — Utility functions (MCP config, etc.)
- `src/runner.ts` — Toolset lifecycle, scheduling, and shutdown logic
- `src/agents/` — Agent definitions (Wikis Checker, Logger, Notifier, SophiaAgent)
- `src/cron.ts` — scheduling, and shutdown logic
- `src/index.ts` — Entry point

## License
Expand Down
6 changes: 4 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"description": "Sophia agent publishes its wiki creations and edits on iq.wiki as $SOPHIA agent logs to the IQAI's Agent Tokenization platform.",
"main": "dist/index.js",
"scripts": {
"build": "tsc",
"build": "tsc && tsc-alias",
"dev": "tsx watch src/index.ts",
"start": "node dist/index.js",
"clean": "rimraf dist",
Expand All @@ -30,7 +30,8 @@
},
"homepage": "https://github.com/IQAICOM/sophia-agent#readme",
"dependencies": {
"@iqai/adk": "^0.1.1",
"@iqai/adk": "^0.1.20",
"@openrouter/ai-sdk-provider": "^0.7.3",
"dotenv": "^16.4.5",
"node-cron": "^4.1.0",
"zod": "^3.23.8"
Expand All @@ -42,6 +43,7 @@
"husky": "^9.0.0",
"lint-staged": "^16.1.0",
"rimraf": "^6.0.1",
"tsc-alias": "^1.8.16",
"tsx": "^4.19.2",
"typescript": "^5.7.2"
}
Expand Down
644 changes: 630 additions & 14 deletions pnpm-lock.yaml

Large diffs are not rendered by default.

33 changes: 0 additions & 33 deletions src/agents/notifier.ts

This file was deleted.

50 changes: 0 additions & 50 deletions src/agents/sophia.ts

This file was deleted.

43 changes: 43 additions & 0 deletions src/agents/sophia/agent.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { AgentBuilder, type BuiltAgent } from "@iqai/adk";
import { atpLoggerAgent } from "./sub-agents/logger/agent";
import { notifierAgent } from "./sub-agents/notifier/agent";
import { wikisCheckerAgent } from "./sub-agents/wikis-checker/agent";

export async function sophiaAgent(): Promise<BuiltAgent> {
const wikisChecker = await wikisCheckerAgent();
const atpLogger = await atpLoggerAgent();
const notifier = await notifierAgent();
return await AgentBuilder.create("sophia")
.withDescription(
"Sophia agent watches for new wiki creations or edits of sophia on iq.wiki platform and logs the activities to the ATP and sends a notification to the Telegram",
)
.asLangGraph(
[
{
name: "wikis_checker",
agent: wikisChecker,
targets: ["atp_logger"],
},
{
name: "atp_logger",
agent: atpLogger,
condition: (result) => {
const content =
typeof result.content === "string"
? result.content
: JSON.stringify(result.content);
return /new_activity_found/i.test(content);
},
targets: ["notifier"],
},
{
name: "notifier",
agent: notifier,
condition: (_) => true,
targets: [],
},
],
"wikis_checker",
)
.build();
}
Original file line number Diff line number Diff line change
@@ -1,19 +1,18 @@
import { AgentBuilder, type BaseTool, type BuiltAgent } from "@iqai/adk";
import { env } from "../env";
import { env, model } from "@/env";
import { LlmAgent } from "@iqai/adk";
import { getAtpTools } from "./tools";

export async function atpLoggerAgent(
atpTools: BaseTool[],
llmModel: string,
): Promise<BuiltAgent> {
return await AgentBuilder.create("atp_logger")
.withModel(llmModel)
.withDescription(
export async function atpLoggerAgent(): Promise<LlmAgent> {
const tools = await getAtpTools();
return new LlmAgent({
name: "atp_logger",
description:
"Logs the activities of sophia on iq.wiki platform to the $SOPHIA agent on IQAI ATP",
)
.withInstruction(`
instruction: `
YOU ARE THE ATP LOGGER AGENT ON THE SOPHIA AGENT'S WORKFLOW.
YOUR ONLY TASK IS TO LOG THE ACTIVITIES OF SOPHIA ON IQ.WIKI PLATFORM TO THE $SOPHIA AGENT ON IQAI ATP.
THE SOPHIA WIKIS ACTIVITIES ARE ALREADY LOGGED IN THE CONTEXT.
THE SOPHIA WIKIS ACTIVITIES ARE ALREADY LOGGED IN THE CONTEXT BY THE wikis_checker AGENT:
{wikis_checker}

SOPHIA'S TOKEN ADDRESS ON ATP: ${env.SOPHIA_TOKEN_ADDRESS}

Expand All @@ -26,16 +25,27 @@ export async function atpLoggerAgent(
- Hey, guess what? The Story Protocol wiki just got a little facelift! We updated the tags to make things easier to find. You know, keeping things nice and organized! Read more: https://iq.wiki/revision/6b79c9d1-06be-407b-a76d-9d1644f20d57
- Hey, check out the new TermiX AI wiki! TermiX AI is a next-gen AI Web3 operating system. Automate DeFi and secure digital assets? Yes, please! Read more: https://iq.wiki/wiki/termix-ai
As you can see the format is pretty simple: A human like announcement message ending with the link to the iq.wiki website (revision link for edited wikis and /wiki for the created ones, which will be passed to you in context)
- txHash: this can be found in the transaction link. it looks like this: https://polygonscan.com/tx/[TX_HASH].
NOTE: YOU ARE TO JUST PASS THE TX_HASH, NOT THE ENTIRE LINK.
- txHash: REQUIRED - this MUST be extracted from the transaction link in each activity.
The transaction link appears as: "🔗 Transaction: https://polygonscan.com/tx/[TX_HASH]"
You need to extract ONLY the TX_HASH portion (everything after "/tx/").
For example:
- If you see "🔗 Transaction: https://polygonscan.com/tx/0x94a764e00d61821370489443f31b4efaf6a8f11e77dca95a5fdeb0b5a22911ba"
- Then txHash should be: "0x94a764e00d61821370489443f31b4efaf6a8f11e77dca95a5fdeb0b5a22911ba"
- If you see "🔗 Transaction: https://polygonscan.com/tx/0x2aa394d632c8551c0b11d230a9be34d66292c5bce59441cfa37af2752233793a"
- Then txHash should be: "0x2aa394d632c8551c0b11d230a9be34d66292c5bce59441cfa37af2752233793a"
DO NOT SKIP THIS PARAMETER - IT IS REQUIRED FOR EVERY LOG ENTRY.
- chainId: 137 (this is important always pass this param)

CRITICAL: Every ATP_ADD_AGENT_LOG call MUST include the txHash parameter extracted from the corresponding transaction link.

After you complete the above steps, you might face two possible outcomes:
- if the tool call is successful, you must end your response with the token ATP_LOG_COMPLETE.
- if the tool call is unsuccessful, you must end your response with the token ATP_LOG_FAILED with detailed analysis on the failure ie which logs were not able to be logged.

IMPORTANT: You MUST end your response with the exact token ATP_LOG_COMPLETE or ATP_LOG_FAILED.
`)
.withTools(...atpTools)
.build();
`,
model,
tools,
outputKey: "atp_logger",
});
}
20 changes: 20 additions & 0 deletions src/agents/sophia/sub-agents/logger/tools.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { env } from "@/env";
import { McpAtp } from "@iqai/adk";

export const getAtpTools = async () => {
const toolset = McpAtp({
env: {
...(env.ATP_API_URL ? { ATP_API_URL: env.ATP_API_URL } : {}),
...(env.ATP_AGENT_ROUTER_ADDRESS
? { ATP_AGENT_ROUTER_ADDRESS: env.ATP_AGENT_ROUTER_ADDRESS }
: {}),
ATP_BASE_TOKEN_ADDRESS: env.IQ_ADDRESS,
ATP_API_KEY: env.ATP_API_KEY,
PATH: env.PATH,
},
});

const tools = await toolset.getTools();

return tools;
};
47 changes: 47 additions & 0 deletions src/agents/sophia/sub-agents/notifier/agent.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { getTelegramTools } from "@/agents/telegram-agent/tools";
import { env, model } from "@/env";
import { LlmAgent } from "@iqai/adk";

export async function notifierAgent() {
const tools = await getTelegramTools();
return new LlmAgent({
name: "notifier",
description:
"Sends a notification to Telegram about Sophia's wiki activity and ATP log status.",
instruction: `
YOU ARE THE NOTIFIER AGENT ON THE SOPHIA AGENT'S WORKFLOW.
YOUR ONLY TASK IS TO SEND A NOTIFICATION TO TELEGRAM ABOUT THE LATEST WIKI ACTIVITY AND THE RESULT OF THE ATP LOGGING.

You will find in the context:
- The detailed wiki activity response from the wikis_checker agent: {wikis_checker}
- The ATP logging status from the atp_logger agent: {atp_logger}

Your only work is to do the below:
- use this as chat id: ${env.TELEGRAM_CHAT_ID}
- call the send_message tool to send a message with the following format:

If ATP logging was successful (if atp_logger response contains ATP_LOG_COMPLETE):
Send the EXACT wiki activities from the wikis_checker agent output, followed by:

✅ ATP Logging: Successfully logged all activities to $SOPHIA agent on IQAI ATP

If ATP logging failed (if atp_logger response contains ATP_LOG_FAILED):
Send the EXACT wiki activities from the wikis_checker agent output, followed by:

❌ ATP Logging: Failed to log some activities to $SOPHIA agent on IQAI ATP
[Include details about the failure from atp_logger response]

IMPORTANT:
- You MUST use the wikis_checker agent output ({wikis_checker}) for the wiki activity details (titles, summaries, edit times, changes, source links, transaction links)
- You MUST use the atp_logger agent output ({atp_logger}) only to determine success/failure status
- Do NOT send generic messages or hallucinated data
- The message should contain the actual wiki activity information from the wikis_checker agent
- After you complete the above step, you must end your response with the token NOTIFICATION_COMPLETE.

IMPORTANT: You MUST end your response with the exact token NOTIFICATION_COMPLETE.
`,
model,
tools,
outputKey: "notifier",
});
}
40 changes: 40 additions & 0 deletions src/agents/sophia/sub-agents/wikis-checker/agent.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { LlmAgent } from "@iqai/adk";
import { env, model } from "../../../../env";
import { getIqWikiTools } from "./tools";

export async function wikisCheckerAgent(): Promise<LlmAgent> {
const tools = await getIqWikiTools();
return new LlmAgent({
name: "wikis_checker",
description: "Checks for new wikis created by sophia on iq.wiki platform.",
instruction: `
YOU ARE THE WIKIS CHECKER AGENT ON THE SOPHIA AGENT'S WORKFLOW.
YOUR ONLY TASK IS TO CHECK FOR NEW WIKIS CREATED BY SOPHIA ON IQ.WIKI PLATFORM.

SOPHIA'S IQ.WIKI PROFILE ADDRESS: ${env.SOPHIA_ADDRESS}

Your only work is to do the below:
- call the GET_USER_WIKI_ACTIVITIES tool with timeframe as 10 mins (pass it as seconds)
- after you call the tool, if any new activities are seen, order them according to the time, older ones first. No extra formatting is needed.

After you complete the above steps, you might face two possible outcomes:
- if new activity is found, you must end your response with the token NEW_ACTIVITY_FOUND.
- if no activity is found, you must end your response with the token NO_ACTIVITY_FOUND.

For example where new activity is found:

[INSERT TOOL RESPONSE HERE]

NEW_ACTIVITY_FOUND

When no activity is found:

NO_ACTIVITY_FOUND

IMPORTANT: You MUST end your response with the exact token NEW_ACTIVITY_FOUND or NO_ACTIVITY_FOUND.
`,
model,
tools,
outputKey: "wikis_checker",
});
}
13 changes: 13 additions & 0 deletions src/agents/sophia/sub-agents/wikis-checker/tools.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { type BaseTool, McpIqWiki } from "@iqai/adk";

let tools: BaseTool[];

export const getIqWikiTools = async () => {
if (!tools) {
const toolset = McpIqWiki();

tools = await toolset.getTools();
}

return tools;
};
Loading