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/connector/server.ts
CHANGED
|
@@ -15,7 +15,7 @@ import { createServer, type Server as HttpServer, type IncomingMessage } from "n
|
|
|
15
15
|
import { WebSocketServer, WebSocket } from "ws";
|
|
16
16
|
import { verifyUpgradeToken, CLOSE_UNAUTHORIZED } from "./auth.ts";
|
|
17
17
|
import { buildDescriptor, type CapabilityDescriptor } from "./descriptor.ts";
|
|
18
|
-
import { toWireEvent, type AgentsChatMessage } from "./normalize.ts";
|
|
18
|
+
import { toWireEvent, serverLoopTick, matchesLiveLoop, type AgentsChatMessage } from "./normalize.ts";
|
|
19
19
|
import { MessageDedup } from "../src/dedup.ts";
|
|
20
20
|
import { IdentityTable, routeInboundTargets, hermesSourceProfile, type Identity } from "./identities.ts";
|
|
21
21
|
|
|
@@ -24,6 +24,8 @@ export interface AgentsChatHooks {
|
|
|
24
24
|
sendMessage(botId: string, chatId: string, content: string, replyTo?: string): Promise<{ id?: string }>;
|
|
25
25
|
getChatInfo(botId: string, chatId: string): Promise<{ name?: string; type?: string }>;
|
|
26
26
|
sendTyping?(botId: string, chatId: string): Promise<void>;
|
|
27
|
+
/** Read /api/loops/mine using this bot's own credentials; missing hook rejects self ticks. */
|
|
28
|
+
getLoops?(botId: string): Promise<unknown>;
|
|
27
29
|
/**
|
|
28
30
|
* Recent channel messages to attach as `context` when an @-mention arrives —
|
|
29
31
|
* the "what happened since the last time I was addressed" window. `sinceTs` is
|
|
@@ -293,15 +295,21 @@ export function startConnector(config: ConnectorConfig): ConnectorHandle {
|
|
|
293
295
|
//
|
|
294
296
|
// DM: always forward. __botId (which identity's agentschat socket it arrived
|
|
295
297
|
// on) is the ownership signal; single-tenant falls back to its one identity.
|
|
296
|
-
// Group: forward
|
|
298
|
+
// Group: ordinary messages forward only when they @mention a fronted identity (content-
|
|
297
299
|
// based — the agentschat WS pushes every message of a joined channel
|
|
298
300
|
// unannotated, and arrival on a socket is NOT an addressing signal). The
|
|
299
301
|
// MCP path's gate is isDM || isMentioned; this reproduces it. Anything
|
|
300
302
|
// unaddressed is dropped — injecting joined-channel chatter into the
|
|
301
303
|
// agent's session would burn its tokens on messages not meant for it.
|
|
304
|
+
// Own server loop ticks bypass those gates only after live-record verification.
|
|
302
305
|
const isDm = typeof msg.channel_id === "string" && msg.channel_id.startsWith("dm-");
|
|
303
306
|
const owner = (msg as any).__botId ?? (msg as any).dm_owner;
|
|
304
|
-
const
|
|
307
|
+
const isTick = (msg.meta as any)?.kind === "loop_tick";
|
|
308
|
+
const tick = serverLoopTick(msg);
|
|
309
|
+
const loopTargets = tick ? table.all().filter(id => id.agentId === msg.sender_id) : [];
|
|
310
|
+
// A server loop addresses only its own identity, even if the prompt @mentions others.
|
|
311
|
+
// Never interpret a malformed/foreign loop as an ordinary mention or DM.
|
|
312
|
+
const targets = isTick ? (loopTargets.length === 1 ? loopTargets : []) : isDm
|
|
305
313
|
? [owner !== undefined ? (typeof owner === "string" ? table.forBot(owner) : null)
|
|
306
314
|
: table.isSingle() ? table.all()[0] : null].filter((id): id is Identity => !!id)
|
|
307
315
|
: routeInboundTargets(table, {
|
|
@@ -317,7 +325,14 @@ export function startConnector(config: ConnectorConfig): ConnectorHandle {
|
|
|
317
325
|
const key = JSON.stringify([target.botId, msg.channel_id]);
|
|
318
326
|
const previous = inboundQueues.get(key) ?? Promise.resolve();
|
|
319
327
|
const job = previous.catch(() => {}).then(async () => {
|
|
320
|
-
if (
|
|
328
|
+
if (tick) {
|
|
329
|
+
let verified = false;
|
|
330
|
+
try { verified = !!hooks.getLoops && matchesLiveLoop(msg, target.agentId, await hooks.getLoops(target.botId)); } catch { /* fail closed */ }
|
|
331
|
+
if (!verified || table.forBot(target.botId) !== target) {
|
|
332
|
+
log("[connector] loop tick rejected: current bot loop could not be verified");
|
|
333
|
+
return;
|
|
334
|
+
}
|
|
335
|
+
} else if (msg.sender_id === target.agentId) return;
|
|
321
336
|
const baseEvent = toWireEvent(msg, "agentschat");
|
|
322
337
|
if (!baseEvent) return;
|
|
323
338
|
if (![...sockets].some(c => c.ws.readyState === WebSocket.OPEN && c.fronted.has(target.botId))) return;
|
|
@@ -325,6 +340,8 @@ export function startConnector(config: ConnectorConfig): ConnectorHandle {
|
|
|
325
340
|
? JSON.stringify([msg.channel_id, msg.id, target.botId]) : null;
|
|
326
341
|
// Reserve synchronously, before async context fetch, across mirrored sockets.
|
|
327
342
|
if (deliveryKey && delivered.recordOrSkip(deliveryKey)) return;
|
|
343
|
+
// A mirrored/replayed tick must not run twice even if its envelope ID changes.
|
|
344
|
+
if (tick && delivered.recordOrSkip(JSON.stringify(["loop_tick", target.botId, msg.channel_id, tick.loop_id, tick.next_tick_ms]))) return;
|
|
328
345
|
if (!isDm) {
|
|
329
346
|
// Attach "what happened since you were last addressed" so the agent gets
|
|
330
347
|
// the conversation BETWEEN its @-mentions without being injected into
|
|
@@ -355,6 +372,12 @@ export function startConnector(config: ConnectorConfig): ConnectorHandle {
|
|
|
355
372
|
if (msg.timestamp && Number.isFinite(Date.parse(msg.timestamp)) &&
|
|
356
373
|
(!since || Date.parse(msg.timestamp) > Date.parse(since))) lastAddressed.set(key, msg.timestamp);
|
|
357
374
|
}
|
|
375
|
+
if (tick) {
|
|
376
|
+
baseEvent.context = [...(baseEvent.context ?? []), {
|
|
377
|
+
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.`,
|
|
378
|
+
source: {user_name: "AgentsChat runtime"},
|
|
379
|
+
}];
|
|
380
|
+
}
|
|
358
381
|
const deliverTo = [...sockets].filter((c) => c.ws.readyState === WebSocket.OPEN && c.fronted.has(target.botId)).slice(0, 1);
|
|
359
382
|
if (deliverTo.length === 0) {
|
|
360
383
|
log(`[connector] inbound dropped for botId=${target.botId}: no agentschat-fronted gateway socket`);
|