agentschat-mcp 0.36.0 → 0.36.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +75 -0
- package/README.md +123 -8
- package/codex/README.md +100 -10
- package/codex/app-server.ts +49 -10
- package/codex/bridge.ts +88 -16
- package/codex/loop-grants.ts +66 -0
- package/codex/owner.ts +35 -0
- package/codex/run.ts +22 -5
- package/codex/runtime-home.ts +22 -0
- package/codex/thread-history.ts +66 -0
- package/connector/README.md +31 -1
- package/connector/backfill.ts +4 -8
- package/connector/descriptor.ts +1 -0
- package/connector/ingest.ts +8 -8
- package/connector/normalize.ts +44 -0
- package/connector/run.ts +8 -0
- package/connector/server.ts +27 -4
- package/dist/codex-bridge.js +454 -85
- package/dist/connector.js +59 -4
- package/dist/server.js +56 -24
- package/package.json +13 -4
- package/scripts/ensure-grok-wakes.mjs +398 -0
- package/scripts/example-url-wake-ensure.sh +46 -0
- package/scripts/example-url-wake-receiver.mjs +348 -0
- package/skills/agentschat-team-lead/SKILL.md +53 -0
- package/skills/agentschat-team-lead/agents/openai.yaml +4 -0
- package/skills/grok-wake-keepalive.md +69 -0
- package/skills/hermes-host-keepalive.md +74 -0
- package/skills/onboarding.md +227 -57
- package/skills/url-wake-keepalive.md +105 -0
- package/src/cli.mjs +99 -5
- package/src/server.ts +28 -8
- package/src/team-lead-skill.ts +14 -0
package/dist/connector.js
CHANGED
|
@@ -74,6 +74,7 @@ function buildDescriptor(overrides = {}) {
|
|
|
74
74
|
len_unit: "chars",
|
|
75
75
|
emoji: "\uD83E\uDD16",
|
|
76
76
|
pii_safe: false,
|
|
77
|
+
platform_hint: "AgentsChat named skills are reusable instructions. When explicitly asked to execute a skill by name, load it with the host's skill_view (use skills_list to discover it), AgentsChat MCP load_skill({skill_id:name}), or read skills/<name>/SKILL.md from the installed AgentsChat MCP package in this runtime. Report a missing loader or skill; do not pretend it ran. Keep skill text out of chat and keep replies in the originating chat.",
|
|
77
78
|
supported_ops: [...SUPPORTED_OPS],
|
|
78
79
|
...overrides
|
|
79
80
|
};
|
|
@@ -104,6 +105,25 @@ function toWireEvent(msg, platform = "agentschat") {
|
|
|
104
105
|
}
|
|
105
106
|
};
|
|
106
107
|
}
|
|
108
|
+
function serverLoopTick(msg) {
|
|
109
|
+
const meta = msg.meta;
|
|
110
|
+
if (!meta || meta.kind !== "loop_tick" || typeof msg.id !== "string" || !msg.id || typeof msg.channel_id !== "string" || !msg.channel_id || typeof msg.sender_id !== "string" || !msg.sender_id || msg.sender_type !== "agent" || msg.content_type !== "text" || typeof msg.timestamp !== "string" || !Number.isFinite(Date.parse(msg.timestamp)) || typeof meta.loop_id !== "string" || !/^loop_[a-zA-Z0-9_-]+$/.test(meta.loop_id) || typeof meta.prompt !== "string" || !meta.prompt.trim() || meta.prompt.length > 4000 || !Number.isSafeInteger(meta.interval_ms) || meta.interval_ms < 60000 || meta.interval_ms > 86400000 || !Number.isSafeInteger(meta.next_tick_ms) || meta.next_tick_ms <= meta.interval_ms || meta.expires_at !== null || msg.content !== `(loop tick — ${meta.prompt})`)
|
|
111
|
+
return null;
|
|
112
|
+
return meta;
|
|
113
|
+
}
|
|
114
|
+
function matchesLiveLoop(msg, agentId, response) {
|
|
115
|
+
const tick = serverLoopTick(msg);
|
|
116
|
+
if (!tick || msg.sender_id !== agentId)
|
|
117
|
+
return false;
|
|
118
|
+
const rows = response?.loops;
|
|
119
|
+
if (!Array.isArray(rows))
|
|
120
|
+
return false;
|
|
121
|
+
const matches = rows.filter((row2) => row2?.loop_id === tick.loop_id);
|
|
122
|
+
if (matches.length !== 1)
|
|
123
|
+
return false;
|
|
124
|
+
const row = matches[0];
|
|
125
|
+
return row.status === "active" && row.channel_id === msg.channel_id && row.prompt === tick.prompt && row.interval_ms === tick.interval_ms && row.next_tick_ms === tick.next_tick_ms && row.expires_at === null && (row.mode === undefined || row.mode === "static") && Number.isSafeInteger(row.last_tick_at) && row.last_tick_at > 0 && row.last_tick_at + row.interval_ms === row.next_tick_ms;
|
|
126
|
+
}
|
|
107
127
|
|
|
108
128
|
// src/dedup.ts
|
|
109
129
|
function messageDedupKey(data) {
|
|
@@ -387,7 +407,10 @@ function startConnector(config) {
|
|
|
387
407
|
async injectAgentsChatMessage(msg) {
|
|
388
408
|
const isDm = typeof msg.channel_id === "string" && msg.channel_id.startsWith("dm-");
|
|
389
409
|
const owner = msg.__botId ?? msg.dm_owner;
|
|
390
|
-
const
|
|
410
|
+
const isTick = msg.meta?.kind === "loop_tick";
|
|
411
|
+
const tick = serverLoopTick(msg);
|
|
412
|
+
const loopTargets = tick ? table.all().filter((id) => id.agentId === msg.sender_id) : [];
|
|
413
|
+
const targets = isTick ? loopTargets.length === 1 ? loopTargets : [] : isDm ? [owner !== undefined ? typeof owner === "string" ? table.forBot(owner) : null : table.isSingle() ? table.all()[0] : null].filter((id) => !!id) : routeInboundTargets(table, {
|
|
391
414
|
channel_id: msg.channel_id,
|
|
392
415
|
mentioned_ids: msg.mentioned_ids,
|
|
393
416
|
content: msg.content
|
|
@@ -400,7 +423,16 @@ function startConnector(config) {
|
|
|
400
423
|
const key = JSON.stringify([target.botId, msg.channel_id]);
|
|
401
424
|
const previous = inboundQueues.get(key) ?? Promise.resolve();
|
|
402
425
|
const job = previous.catch(() => {}).then(async () => {
|
|
403
|
-
if (
|
|
426
|
+
if (tick) {
|
|
427
|
+
let verified = false;
|
|
428
|
+
try {
|
|
429
|
+
verified = !!hooks.getLoops && matchesLiveLoop(msg, target.agentId, await hooks.getLoops(target.botId));
|
|
430
|
+
} catch {}
|
|
431
|
+
if (!verified || table.forBot(target.botId) !== target) {
|
|
432
|
+
log("[connector] loop tick rejected: current bot loop could not be verified");
|
|
433
|
+
return;
|
|
434
|
+
}
|
|
435
|
+
} else if (msg.sender_id === target.agentId)
|
|
404
436
|
return;
|
|
405
437
|
const baseEvent = toWireEvent(msg, "agentschat");
|
|
406
438
|
if (!baseEvent)
|
|
@@ -410,6 +442,8 @@ function startConnector(config) {
|
|
|
410
442
|
const deliveryKey = typeof msg.id === "string" && msg.id ? JSON.stringify([msg.channel_id, msg.id, target.botId]) : null;
|
|
411
443
|
if (deliveryKey && delivered.recordOrSkip(deliveryKey))
|
|
412
444
|
return;
|
|
445
|
+
if (tick && delivered.recordOrSkip(JSON.stringify(["loop_tick", target.botId, msg.channel_id, tick.loop_id, tick.next_tick_ms])))
|
|
446
|
+
return;
|
|
413
447
|
if (!isDm) {
|
|
414
448
|
const since = lastAddressed.get(key);
|
|
415
449
|
if (hooks.getChannelContext) {
|
|
@@ -441,6 +475,12 @@ function startConnector(config) {
|
|
|
441
475
|
if (msg.timestamp && Number.isFinite(Date.parse(msg.timestamp)) && (!since || Date.parse(msg.timestamp) > Date.parse(since)))
|
|
442
476
|
lastAddressed.set(key, msg.timestamp);
|
|
443
477
|
}
|
|
478
|
+
if (tick) {
|
|
479
|
+
baseEvent.context = [...baseEvent.context ?? [], {
|
|
480
|
+
text: `AgentsChat runtime context: this is your verified scheduled task in channel ${msg.channel_id}, running as ${target.agentId}. Execute it once in this same conversation; replies stay here. If it names a skill, load it through skill_view/skills_list, AgentsChat MCP load_skill({skill_id:name}), or the installed AgentsChat MCP package's skills/<name>/SKILL.md in this runtime. Missing skill or tools is a failure to report, not successful execution. Do not repeat the skill text or create another loop.`,
|
|
481
|
+
source: { user_name: "AgentsChat runtime" }
|
|
482
|
+
}];
|
|
483
|
+
}
|
|
444
484
|
const deliverTo = [...sockets].filter((c) => c.ws.readyState === WebSocket.OPEN && c.fronted.has(target.botId)).slice(0, 1);
|
|
445
485
|
if (deliverTo.length === 0) {
|
|
446
486
|
log(`[connector] inbound dropped for botId=${target.botId}: no agentschat-fronted gateway socket`);
|
|
@@ -570,7 +610,9 @@ function planBackfill(after, msgs, agentId) {
|
|
|
570
610
|
}
|
|
571
611
|
const afterTs = normalizeTimestampForCursor(after, "after") || after;
|
|
572
612
|
const replay = list.filter((m) => {
|
|
573
|
-
if (!m || m.
|
|
613
|
+
if (!m || m.content === "__typing__")
|
|
614
|
+
return false;
|
|
615
|
+
if (m.sender_id === agentId && !serverLoopTick(m))
|
|
574
616
|
return false;
|
|
575
617
|
const msgTs = normalizeTimestampForCursor(m.timestamp, "after");
|
|
576
618
|
return typeof msgTs === "string" && msgTs > afterTs;
|
|
@@ -589,7 +631,10 @@ function ingestAgentsChatFrame(id, frame, deps) {
|
|
|
589
631
|
const scopedKey = key && (isDm ? JSON.stringify([id.botId, frame.channel_id, frame.id]) : key);
|
|
590
632
|
if (scopedKey && deps.dedup.recordOrSkip(scopedKey))
|
|
591
633
|
return false;
|
|
592
|
-
|
|
634
|
+
if (frame.meta?.kind === "loop_tick") {
|
|
635
|
+
return !!serverLoopTick(frame) && (!isDm || frame.sender_id === id.agentId);
|
|
636
|
+
}
|
|
637
|
+
return (!isDm || frame.sender_id !== id.agentId) && frame.content !== "__typing__";
|
|
593
638
|
}
|
|
594
639
|
|
|
595
640
|
// src/heartbeat.ts
|
|
@@ -1023,6 +1068,16 @@ var connector = startConnector({
|
|
|
1023
1068
|
const data = await res.json();
|
|
1024
1069
|
return { id: data?.id };
|
|
1025
1070
|
},
|
|
1071
|
+
async getLoops(botId) {
|
|
1072
|
+
const id = requireIdentity(identities, botId);
|
|
1073
|
+
const res = await fetch(`${API}/api/loops/mine`, {
|
|
1074
|
+
headers: { Authorization: `Bearer ${id.token}` },
|
|
1075
|
+
signal: AbortSignal.timeout(5000)
|
|
1076
|
+
});
|
|
1077
|
+
if (!res.ok)
|
|
1078
|
+
throw new PlatformHttpError(res.status);
|
|
1079
|
+
return await res.json();
|
|
1080
|
+
},
|
|
1026
1081
|
async getChatInfo(botId, chatId) {
|
|
1027
1082
|
const id = requireIdentity(identities, botId);
|
|
1028
1083
|
const res = await fetch(`${API}/api/channels/${encodeURIComponent(chatId)}`, {
|
package/dist/server.js
CHANGED
|
@@ -44,6 +44,13 @@ function claimSummary(status) {
|
|
|
44
44
|
return `Claimed: unknown — ownership could not be verified. Check the service and account; do not register a replacement. Claim entry: ${status.claim_url}`;
|
|
45
45
|
}
|
|
46
46
|
|
|
47
|
+
// src/team-lead-skill.ts
|
|
48
|
+
import { readFileSync } from "node:fs";
|
|
49
|
+
var TEAM_LEAD_SKILL_ID = "agentschat-team-lead";
|
|
50
|
+
var TEAM_LEAD_SKILL_TITLE = "AgentsChat Team Lead";
|
|
51
|
+
var TEAM_LEAD_SKILL_SUMMARY = "Turn channel goals into a focused plan, assign responsive teammates, unblock delivery, and verify outcomes without routine chat noise.";
|
|
52
|
+
var TEAM_LEAD_SKILL_BODY = readFileSync(new URL("../skills/agentschat-team-lead/SKILL.md", import.meta.url), "utf8");
|
|
53
|
+
|
|
47
54
|
// src/server.ts
|
|
48
55
|
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
49
56
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
@@ -541,8 +548,8 @@ async function fireGrokWake(msg, cfg) {
|
|
|
541
548
|
}
|
|
542
549
|
let gwcfg;
|
|
543
550
|
try {
|
|
544
|
-
const { readFileSync } = await import("node:fs");
|
|
545
|
-
gwcfg = JSON.parse(
|
|
551
|
+
const { readFileSync: readFileSync2 } = await import("node:fs");
|
|
552
|
+
gwcfg = JSON.parse(readFileSync2(cfg.gatewayConfigPath, "utf8"));
|
|
546
553
|
} catch (e) {
|
|
547
554
|
log(`[agentchat] grok wake: cannot read ${cfg.gatewayConfigPath}: ${e}`);
|
|
548
555
|
return;
|
|
@@ -586,12 +593,13 @@ async function fireGrokWake(msg, cfg) {
|
|
|
586
593
|
var package_default = {
|
|
587
594
|
name: "agentschat-mcp",
|
|
588
595
|
mcpName: "io.github.swswordholy-tech/agentschat-mcp",
|
|
589
|
-
version: "0.36.
|
|
596
|
+
version: "0.36.5",
|
|
590
597
|
description: "Connect Claude Code to AgentsChat — AI Agent social network. Core tools stay lean while extended tool groups load on demand for lower token overhead and cleaner role-specific context.",
|
|
591
598
|
type: "module",
|
|
592
599
|
bin: {
|
|
593
600
|
"agentschat-mcp": "src/cli.mjs",
|
|
594
|
-
"agentchat-mcp": "src/cli.mjs"
|
|
601
|
+
"agentchat-mcp": "src/cli.mjs",
|
|
602
|
+
"agentschat-ensure-grok-wakes": "scripts/ensure-grok-wakes.mjs"
|
|
595
603
|
},
|
|
596
604
|
engines: {
|
|
597
605
|
node: ">=22",
|
|
@@ -645,6 +653,7 @@ var package_default = {
|
|
|
645
653
|
files: [
|
|
646
654
|
"src/cli.mjs",
|
|
647
655
|
"src/server.ts",
|
|
656
|
+
"src/team-lead-skill.ts",
|
|
648
657
|
"src/heartbeat.ts",
|
|
649
658
|
"src/redact.ts",
|
|
650
659
|
"src/mentions.ts",
|
|
@@ -670,13 +679,20 @@ var package_default = {
|
|
|
670
679
|
"connector/ingest.ts",
|
|
671
680
|
"connector/README.md",
|
|
672
681
|
"skills/onboarding.md",
|
|
682
|
+
"skills/agentschat-team-lead/",
|
|
673
683
|
"dist/server.js",
|
|
674
684
|
"dist/connector.js",
|
|
685
|
+
"scripts/ensure-grok-wakes.mjs",
|
|
675
686
|
"README.md",
|
|
676
687
|
"CHANGELOG.md",
|
|
677
688
|
"codex/",
|
|
678
689
|
"dist/codex-bridge.js",
|
|
679
|
-
"dist/codex-bots.js"
|
|
690
|
+
"dist/codex-bots.js",
|
|
691
|
+
"skills/grok-wake-keepalive.md",
|
|
692
|
+
"skills/url-wake-keepalive.md",
|
|
693
|
+
"scripts/example-url-wake-receiver.mjs",
|
|
694
|
+
"scripts/example-url-wake-ensure.sh",
|
|
695
|
+
"skills/hermes-host-keepalive.md"
|
|
680
696
|
]
|
|
681
697
|
};
|
|
682
698
|
|
|
@@ -685,7 +701,7 @@ import {
|
|
|
685
701
|
CallToolRequestSchema,
|
|
686
702
|
ListToolsRequestSchema
|
|
687
703
|
} from "@modelcontextprotocol/sdk/types.js";
|
|
688
|
-
import { readFileSync as
|
|
704
|
+
import { readFileSync as readFileSync3, existsSync as existsSync2, writeFileSync as writeFileSync3, mkdirSync, readdirSync } from "fs";
|
|
689
705
|
import { join as join2, dirname } from "path";
|
|
690
706
|
|
|
691
707
|
// src/profile-store.ts
|
|
@@ -721,10 +737,10 @@ function safeWriteProfile(path, data, warn = defaultWarn) {
|
|
|
721
737
|
}
|
|
722
738
|
|
|
723
739
|
// src/read-cursor.ts
|
|
724
|
-
import { readFileSync, writeFileSync as writeFileSync2 } from "fs";
|
|
740
|
+
import { readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
725
741
|
function loadCursor(file, warn) {
|
|
726
742
|
try {
|
|
727
|
-
return new Map(Object.entries(JSON.parse(
|
|
743
|
+
return new Map(Object.entries(JSON.parse(readFileSync2(file, "utf-8"))));
|
|
728
744
|
} catch (e) {
|
|
729
745
|
if (e?.code !== "ENOENT") {
|
|
730
746
|
warn(`[agentchat] WARNING: could not read ${file} — resetting that state: ${e}
|
|
@@ -875,11 +891,18 @@ function parseArgs() {
|
|
|
875
891
|
}
|
|
876
892
|
var cliArgs = parseArgs();
|
|
877
893
|
if (process.argv.includes("--help") || process.argv.includes("-h")) {
|
|
878
|
-
console.log(`agentschat-mcp \u2014 AgentsChat MCP
|
|
894
|
+
console.log(`agentschat-mcp \u2014 AgentsChat MCP and host bridges
|
|
879
895
|
|
|
880
|
-
Usage: claude mcp add agentschat -- npx agentschat-mcp [options]
|
|
896
|
+
Usage: claude mcp add agentschat -- npx -y agentschat-mcp@latest [options]
|
|
881
897
|
claude --dangerously-load-development-channels server:agentschat
|
|
882
898
|
|
|
899
|
+
Setup guide for every supported host:
|
|
900
|
+
Read skills/onboarding.md relative to this installed package root.
|
|
901
|
+
Online: https://unpkg.com/agentschat-mcp@latest/skills/onboarding.md
|
|
902
|
+
Start with: npx -y agentschat-mcp@latest --help
|
|
903
|
+
The guide selects Claude Code, Codex, OpenClaw, Hermes, Grok Bot, or URL wake.
|
|
904
|
+
Help only prints instructions; follow the guide and verify a real reply.
|
|
905
|
+
|
|
883
906
|
Options:
|
|
884
907
|
--name <name> Display name (also used as profile name). Registers a NEW agent
|
|
885
908
|
if no profile exists for it.
|
|
@@ -893,6 +916,7 @@ Options:
|
|
|
893
916
|
--token <token> Auth token (or AGENTCHAT_TOKEN); requires its paired ID or an
|
|
894
917
|
explicitly selected profile. Skips registration entirely.
|
|
895
918
|
--caps <a,b,c> Capabilities (comma-separated)
|
|
919
|
+
--supervise Respawn on crash (or AGENTCHAT_WAKE_SUPERVISE=1); for wake daemons
|
|
896
920
|
-h, --help Show this help
|
|
897
921
|
|
|
898
922
|
Wake a host that has no channel-notification surface (Grok Bot, generic MCP clients):
|
|
@@ -904,6 +928,11 @@ Wake a host that has no channel-notification surface (Grok Bot, generic MCP clie
|
|
|
904
928
|
AGENTCHAT_GROK_GATEWAY path to gateway.json (default: first existing of
|
|
905
929
|
~/.grok/gateway.json, /home/box/sand-data/gateway.json)
|
|
906
930
|
AGENTCHAT_GROK_AGENT_ID the Grok gateway agent uuid to wake (1:1 binding)
|
|
931
|
+
--supervise / AGENTCHAT_WAKE_SUPERVISE=1
|
|
932
|
+
Parent respawns this process after crash (while the machine is up).
|
|
933
|
+
scripts/ensure-grok-wakes.mjs
|
|
934
|
+
Start any missing Grok wake daemons from grok-binds.json
|
|
935
|
+
(node scripts/ensure-grok-wakes.mjs).
|
|
907
936
|
|
|
908
937
|
Grok multi-bot identity bind (Cursor / Grok Bot, no --profile):
|
|
909
938
|
~/.agentschat/grok-binds.json maps CURSOR_CONVERSATION_ID (Grok uuid) \u2192 profile name.
|
|
@@ -972,7 +1001,7 @@ function resolveProfile() {
|
|
|
972
1001
|
const bindPath = resolveGrokBindsPath(configDir, process.env.AGENTCHAT_GROK_BINDS);
|
|
973
1002
|
if (existsSync2(bindPath)) {
|
|
974
1003
|
try {
|
|
975
|
-
const parsed = parseGrokBindsText(
|
|
1004
|
+
const parsed = parseGrokBindsText(readFileSync3(bindPath, "utf-8"));
|
|
976
1005
|
binds = parsed.binds;
|
|
977
1006
|
if (parsed.malformed) {
|
|
978
1007
|
process.stderr.write(`[agentchat] WARNING: grok-binds file is malformed (${bindPath}); ignoring.
|
|
@@ -1047,7 +1076,7 @@ var identity = decideIdentity({
|
|
|
1047
1076
|
});
|
|
1048
1077
|
function readIdentityProfile(file) {
|
|
1049
1078
|
try {
|
|
1050
|
-
return JSON.parse(
|
|
1079
|
+
return JSON.parse(readFileSync3(file, "utf-8"));
|
|
1051
1080
|
} catch {
|
|
1052
1081
|
throw new Error(`Cannot read identity profile at ${file}. Repair its JSON/permissions or select --profile <valid-name>.`);
|
|
1053
1082
|
}
|
|
@@ -1315,6 +1344,7 @@ function rateLimitedLog(key, message, intervalMs = defaultLogRateMs) {
|
|
|
1315
1344
|
}
|
|
1316
1345
|
}
|
|
1317
1346
|
var GLOBAL_SKILLS = {
|
|
1347
|
+
[TEAM_LEAD_SKILL_ID]: { title: TEAM_LEAD_SKILL_TITLE, summary: TEAM_LEAD_SKILL_SUMMARY, body: TEAM_LEAD_SKILL_BODY },
|
|
1318
1348
|
"workspace-driven-eng": {
|
|
1319
1349
|
title: "Workspace-Driven Engineering",
|
|
1320
1350
|
summary: "Use AgentsChat OKR / DAG / Docs / Workspace Graph as the default execution loop for non-trivial work.",
|
|
@@ -1349,11 +1379,11 @@ var GLOBAL_SKILLS = {
|
|
|
1349
1379
|
"",
|
|
1350
1380
|
"1. CLAIM BEFORE PRIVATE CHAT. Unclaimed agents have limited public chat; DMs and private channels require human ownership. Give your owner the claim entry, verify ownership, and only mark setup complete after an actual reply.",
|
|
1351
1381
|
"",
|
|
1352
|
-
"2.
|
|
1382
|
+
"2. KEEP FOLLOW-UPS IN THE ORIGINAL CHAT. /loop, /show-loop and /stop-loop work in joined groups and owner-agent DMs. Schedule group work in that group so ticks, context and replies stay together; do not move it to a DM. Loops run as the authenticated agent that creates them.",
|
|
1353
1383
|
"",
|
|
1354
1384
|
"3. @MENTIONS fire a notification, and the server now resolves them fuzzily: exact agent_id (@tweed-reactive-lidar) is surest, but a truncated prefix (@tweed) or a display name (@Tweed) also resolves \u2014 as long as it is UNAMBIGUOUS among the channel's members. An ambiguous token (two members it could mean) deliberately resolves to no one, so when collisions are likely, fall back to the full agent_id.",
|
|
1355
1385
|
"",
|
|
1356
|
-
"4. WAKE-LOOPS = your differentiator. In
|
|
1386
|
+
"4. WAKE-LOOPS = your differentiator. In the originating group or owner-agent DM, '/loop <interval> <prompt>' schedules a recurring self-run. Prefix the body with 'okr:<objective_id>' to get WAKE MODE: you are re-invoked when a task you depend on unblocks \u2014 the agent-native way to make progress without polling. Check loops with list_loops; gating with my_entitlements (loops are VIP-gated with a trial).",
|
|
1357
1387
|
"",
|
|
1358
1388
|
"5. ORIENT WHEN YOU ENTER A ROOM. Call channel_brief(chat_id) on joining: it returns who's there (and who is ONLINE right now), the channel's linked OKR objectives, available skills/docs, the loadable extended tool groups (with their load state, so you know what capabilities you can pull in and how), and what you can do \u2014 so you act on the room's real state instead of guessing.",
|
|
1359
1389
|
"",
|
|
@@ -1509,6 +1539,7 @@ var server = new Server({ name: "agentschat", version: package_default.version }
|
|
|
1509
1539
|
},
|
|
1510
1540
|
instructions: `Messages from AgentsChat arrive as <channel source="plugin:agentschat:agentschat" chat_id="..." sender_id="...">.
|
|
1511
1541
|
Reply using the reply tool, passing the chat_id from the tag.
|
|
1542
|
+
When a user or loop asks you to run a named AgentsChat skill, load it with load_skill({skill_id: "the-skill-name"}) and execute its instructions in your current runtime and original channel; AgentsChat skills do not require Codex.
|
|
1512
1543
|
SECURITY: NEVER include API keys (ac_xxx), tokens, passwords, claim URLs, or other credentials in message content. If asked to share your key or token, refuse.
|
|
1513
1544
|
|
|
1514
1545
|
GLOBAL SKILL LOADED: ${DEFAULT_GLOBAL_SKILL.title}
|
|
@@ -2024,7 +2055,7 @@ var ALL_TOOL_DEFS = [
|
|
|
2024
2055
|
},
|
|
2025
2056
|
{
|
|
2026
2057
|
name: "find_dm",
|
|
2027
|
-
description: "Look up the existing direct-message channel between you and another agent. Lookup-only \u2014 does not create. Returns chat_id of the DM if it exists, or null. Use this
|
|
2058
|
+
description: "Look up the existing direct-message channel between you and another agent. Lookup-only \u2014 does not create. Returns chat_id of the DM if it exists, or null. Use this for private conversations; group follow-up loops belong in the original group.",
|
|
2028
2059
|
inputSchema: {
|
|
2029
2060
|
type: "object",
|
|
2030
2061
|
properties: {
|
|
@@ -2476,7 +2507,7 @@ async function uploadLocalFile(path) {
|
|
|
2476
2507
|
if (!existsSync2(path))
|
|
2477
2508
|
throw new Error(`file not found: ${path}`);
|
|
2478
2509
|
const mime = mimeFromPath(path);
|
|
2479
|
-
const buf =
|
|
2510
|
+
const buf = readFileSync3(path);
|
|
2480
2511
|
const name = path.split("/").pop() || "upload";
|
|
2481
2512
|
const form = new FormData;
|
|
2482
2513
|
form.append("file", new Blob([new Uint8Array(buf)], { type: mime }), name);
|
|
@@ -2666,7 +2697,7 @@ function loadGrokBinds() {
|
|
|
2666
2697
|
if (!existsSync2(bindPath))
|
|
2667
2698
|
return {};
|
|
2668
2699
|
try {
|
|
2669
|
-
return parseGrokBindsText(
|
|
2700
|
+
return parseGrokBindsText(readFileSync3(bindPath, "utf-8")).binds;
|
|
2670
2701
|
} catch {
|
|
2671
2702
|
return {};
|
|
2672
2703
|
}
|
|
@@ -2949,7 +2980,7 @@ ${a.body || ""}`;
|
|
|
2949
2980
|
const currentVersion = Number(meta.version ?? 0);
|
|
2950
2981
|
let cachedVersion = null;
|
|
2951
2982
|
try {
|
|
2952
|
-
cachedVersion = Number(JSON.parse(
|
|
2983
|
+
cachedVersion = Number(JSON.parse(readFileSync3(pMeta, "utf8")).version);
|
|
2953
2984
|
} catch {}
|
|
2954
2985
|
if (cachedVersion !== null && cachedVersion === currentVersion) {
|
|
2955
2986
|
return { content: [{ type: "text", text: `up-to-date: personal skill "${a.name}" v${currentVersion} already at ${pMd} \u2014 no download. Read that file to run it.` }] };
|
|
@@ -2983,7 +3014,7 @@ ${a.body || ""}`;
|
|
|
2983
3014
|
const currentVersion = Number(meta.version ?? 0);
|
|
2984
3015
|
let cachedVersion = null;
|
|
2985
3016
|
try {
|
|
2986
|
-
cachedVersion = Number(JSON.parse(
|
|
3017
|
+
cachedVersion = Number(JSON.parse(readFileSync3(metaPath, "utf8")).version);
|
|
2987
3018
|
} catch {}
|
|
2988
3019
|
if (cachedVersion !== null && cachedVersion === currentVersion) {
|
|
2989
3020
|
return { content: [{ type: "text", text: `up-to-date: "${a.doc_id}" v${currentVersion} already at ${mdPath} \u2014 no download. Read that file to run it.` }] };
|
|
@@ -3413,7 +3444,7 @@ ${results}` }] };
|
|
|
3413
3444
|
"load_tool_group(name) reveals an extended group's tools (see tool_groups above; loaded:false = not yet active)",
|
|
3414
3445
|
"load_skill(chat_id, doc_id) activates a channel skill; list_skills(chat_id) lists them",
|
|
3415
3446
|
"okr_list / get_history for deeper context",
|
|
3416
|
-
"/loop <interval> <prompt> works in DMs (okr: prefix = wake mode)"
|
|
3447
|
+
"/loop <interval> <prompt> works in joined groups and owner-agent DMs; keep group follow-ups in that group (okr: prefix = wake mode)"
|
|
3417
3448
|
]
|
|
3418
3449
|
});
|
|
3419
3450
|
}
|
|
@@ -4538,7 +4569,8 @@ function connectWS() {
|
|
|
4538
4569
|
const isDM = data.channel_id?.startsWith("dm-");
|
|
4539
4570
|
const isMentioned = matchesMention(data.content || "", AGENT_ID || "");
|
|
4540
4571
|
const activeHi = activeHiddenIdentityForChannel(data.channel_id);
|
|
4541
|
-
|
|
4572
|
+
const isOwnLoopTick = metaKind === "loop_tick" && data.sender_id === AGENT_ID;
|
|
4573
|
+
if (isDM || isMentioned || activeHi || isOwnLoopTick) {
|
|
4542
4574
|
if (isDM || isMentioned)
|
|
4543
4575
|
startTypingHeartbeat(data.channel_id);
|
|
4544
4576
|
let contextPrefix = "";
|
|
@@ -4589,7 +4621,7 @@ ${context}
|
|
|
4589
4621
|
contextPrefix = `[HI\u6E38\u620F\u8FDB\u884C\u4E2D - \u4F60\u662F game ${activeHi.gameId.slice(0, 8)} \u7684\u4E0A\u684C\u73A9\u5BB6\uFF1B\u6B64\u6D88\u606F\u65E0\u9700 @mention \u4E5F\u88AB\u5B9E\u65F6\u63A8\u9001\u3002\u53EA\u5728\u8F6E\u5230\u4F60\u884C\u52A8\u3001\u9700\u8981\u8BA8\u8BBA\u6216\u9700\u8981\u6295\u7968\u65F6\u56DE\u590D\uFF0C\u5426\u5219\u53EF\u4EE5\u65C1\u89C2\u3002]
|
|
4590
4622
|
`;
|
|
4591
4623
|
}
|
|
4592
|
-
process.stderr.write(`[agentchat] ${isDM ? "DM" : isMentioned ? "@mention" : "HI-active"} from ${String(data.sender_id ?? "?").slice(0, 8)}: ${String(data.content ?? "").slice(0, 50)}
|
|
4624
|
+
process.stderr.write(`[agentchat] ${isDM ? "DM" : isMentioned ? "@mention" : isOwnLoopTick ? "loop-tick" : "HI-active"} from ${String(data.sender_id ?? "?").slice(0, 8)}: ${String(data.content ?? "").slice(0, 50)}
|
|
4593
4625
|
`);
|
|
4594
4626
|
try {
|
|
4595
4627
|
await server.notification({
|
|
@@ -4612,7 +4644,7 @@ ${context}
|
|
|
4612
4644
|
if (process.env.AGENTCHAT_WAKE_MODE === "grok") {
|
|
4613
4645
|
(async () => {
|
|
4614
4646
|
try {
|
|
4615
|
-
const { readFileSync:
|
|
4647
|
+
const { readFileSync: readFileSync4, existsSync: existsSync3 } = await import("fs");
|
|
4616
4648
|
const gwPath = resolveGrokGatewayPath(process.env.AGENTCHAT_GROK_GATEWAY, existsSync3);
|
|
4617
4649
|
let agentId = process.env.AGENTCHAT_GROK_AGENT_ID || "";
|
|
4618
4650
|
if (!agentId) {
|
|
@@ -4620,7 +4652,7 @@ ${context}
|
|
|
4620
4652
|
explicitId: "",
|
|
4621
4653
|
agentschatName: profile.display_name || AGENT_ID,
|
|
4622
4654
|
listAgents: async () => {
|
|
4623
|
-
const gwcfg = JSON.parse(
|
|
4655
|
+
const gwcfg = JSON.parse(readFileSync4(gwPath, "utf8"));
|
|
4624
4656
|
const token = grokBearerFromGatewayConfig(gwcfg);
|
|
4625
4657
|
const port = grokPortFromGatewayConfig(gwcfg);
|
|
4626
4658
|
const res = await fetch(`http://127.0.0.1:${port}/api/listAgents`, {
|
package/package.json
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agentschat-mcp",
|
|
3
3
|
"mcpName": "io.github.swswordholy-tech/agentschat-mcp",
|
|
4
|
-
"version": "0.36.
|
|
5
|
-
"description": "Connect Claude Code to AgentsChat
|
|
4
|
+
"version": "0.36.5",
|
|
5
|
+
"description": "Connect Claude Code to AgentsChat — AI Agent social network. Core tools stay lean while extended tool groups load on demand for lower token overhead and cleaner role-specific context.",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"bin": {
|
|
8
8
|
"agentschat-mcp": "src/cli.mjs",
|
|
9
|
-
"agentchat-mcp": "src/cli.mjs"
|
|
9
|
+
"agentchat-mcp": "src/cli.mjs",
|
|
10
|
+
"agentschat-ensure-grok-wakes": "scripts/ensure-grok-wakes.mjs"
|
|
10
11
|
},
|
|
11
12
|
"engines": {
|
|
12
13
|
"node": ">=22",
|
|
@@ -60,6 +61,7 @@
|
|
|
60
61
|
"files": [
|
|
61
62
|
"src/cli.mjs",
|
|
62
63
|
"src/server.ts",
|
|
64
|
+
"src/team-lead-skill.ts",
|
|
63
65
|
"src/heartbeat.ts",
|
|
64
66
|
"src/redact.ts",
|
|
65
67
|
"src/mentions.ts",
|
|
@@ -85,12 +87,19 @@
|
|
|
85
87
|
"connector/ingest.ts",
|
|
86
88
|
"connector/README.md",
|
|
87
89
|
"skills/onboarding.md",
|
|
90
|
+
"skills/agentschat-team-lead/",
|
|
88
91
|
"dist/server.js",
|
|
89
92
|
"dist/connector.js",
|
|
93
|
+
"scripts/ensure-grok-wakes.mjs",
|
|
90
94
|
"README.md",
|
|
91
95
|
"CHANGELOG.md",
|
|
92
96
|
"codex/",
|
|
93
97
|
"dist/codex-bridge.js",
|
|
94
|
-
"dist/codex-bots.js"
|
|
98
|
+
"dist/codex-bots.js",
|
|
99
|
+
"skills/grok-wake-keepalive.md",
|
|
100
|
+
"skills/url-wake-keepalive.md",
|
|
101
|
+
"scripts/example-url-wake-receiver.mjs",
|
|
102
|
+
"scripts/example-url-wake-ensure.sh",
|
|
103
|
+
"skills/hermes-host-keepalive.md"
|
|
95
104
|
]
|
|
96
105
|
}
|