555e282cc4
pi-agent-plugin checks / lint (push) Has been cancelled
pi-agent-plugin checks / test (20) (push) Has been cancelled
pi-agent-plugin checks / test (22) (push) Has been cancelled
pi-agent-plugin checks / build (push) Has been cancelled
TypeScript SDK CI / check_changes (push) Has been cancelled
TypeScript SDK CI / changelog_check (push) Has been cancelled
ci / changelog_check (push) Has been cancelled
ci / check_changes (push) Has been cancelled
ci / build_mem0 (3.10) (push) Has been cancelled
ci / build_mem0 (3.11) (push) Has been cancelled
ci / build_mem0 (3.12) (push) Has been cancelled
CLI Node CI / lint (push) Has been cancelled
CLI Node CI / test (20) (push) Has been cancelled
CLI Node CI / test (22) (push) Has been cancelled
CLI Node CI / build (push) Has been cancelled
CLI Python CI / lint (push) Has been cancelled
CLI Python CI / test (3.10) (push) Has been cancelled
CLI Python CI / test (3.11) (push) Has been cancelled
CLI Python CI / test (3.12) (push) Has been cancelled
CLI Python CI / build (push) Has been cancelled
openclaw checks / lint (push) Has been cancelled
openclaw checks / test (20) (push) Has been cancelled
openclaw checks / test (22) (push) Has been cancelled
openclaw checks / build (push) Has been cancelled
opencode-plugin checks / build (push) Has been cancelled
TypeScript SDK CI / build_ts_sdk (20) (push) Has been cancelled
TypeScript SDK CI / build_ts_sdk (22) (push) Has been cancelled
TypeScript SDK CI / integration_ts_sdk (20) (push) Has been cancelled
TypeScript SDK CI / integration_ts_sdk (22) (push) Has been cancelled
108 lines
3.5 KiB
TypeScript
108 lines
3.5 KiB
TypeScript
/**
|
|
* Per-agent memory isolation helpers.
|
|
*
|
|
* Multi-agent setups write/read from separate userId namespaces
|
|
* automatically via sessionKey routing.
|
|
*/
|
|
|
|
// ============================================================================
|
|
// Trigger filtering — skip non-interactive sessions
|
|
// ============================================================================
|
|
|
|
/**
|
|
* Triggers that should NOT run autocapture/autorecall.
|
|
* These are system-initiated sessions (cron jobs, heartbeats, automation
|
|
* pipelines) whose prompts would pollute the user's memory store.
|
|
*/
|
|
const SKIP_TRIGGERS = new Set(["cron", "heartbeat", "automation", "schedule"]);
|
|
|
|
/**
|
|
* Returns true if the session trigger is non-interactive and memory
|
|
* hooks should be skipped entirely.
|
|
*
|
|
* Also detects cron-style session keys (e.g. "agent:main:cron:<id>")
|
|
* as a fallback when the trigger field is not set.
|
|
*/
|
|
export function isNonInteractiveTrigger(
|
|
trigger: string | undefined,
|
|
sessionKey: string | undefined,
|
|
): boolean {
|
|
if (trigger && SKIP_TRIGGERS.has(trigger.toLowerCase())) return true;
|
|
|
|
// Fallback: detect cron/heartbeat from the session key pattern
|
|
if (sessionKey) {
|
|
if (/:cron:/i.test(sessionKey) || /:heartbeat:/i.test(sessionKey))
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Returns true if the session key indicates a subagent (ephemeral) session.
|
|
* Subagent UUIDs are random per-spawn, so their namespaces are always empty
|
|
* on recall and orphaned after capture.
|
|
*/
|
|
export function isSubagentSession(sessionKey: string | undefined): boolean {
|
|
if (!sessionKey) return false;
|
|
return /:subagent:/i.test(sessionKey);
|
|
}
|
|
|
|
/**
|
|
* Parse an agent ID from a session key.
|
|
*
|
|
* OpenClaw session key formats:
|
|
* - Main agent: "agent:main:main"
|
|
* - Subagent: "agent:main:subagent:<uuid>"
|
|
* - Named agent: "agent:<agentId>:<session>"
|
|
*
|
|
* Returns the subagent UUID for subagent sessions, the agentId for
|
|
* non-"main" named agents, or undefined for the main agent session.
|
|
*/
|
|
export function extractAgentId(
|
|
sessionKey: string | undefined,
|
|
): string | undefined {
|
|
if (!sessionKey) return undefined;
|
|
|
|
// Check for subagent pattern: "agent:<parent>:subagent:<uuid>"
|
|
const subagentMatch = sessionKey.match(/:subagent:([^:]+)$/);
|
|
if (subagentMatch?.[1]) return `subagent-${subagentMatch[1]}`;
|
|
|
|
// Check for named agent pattern: "agent:<agentId>:<session>"
|
|
const match = sessionKey.match(/^agent:([^:]+):/);
|
|
const agentId = match?.[1];
|
|
// "main" is the primary session — fall back to configured userId
|
|
if (!agentId || agentId === "main") return undefined;
|
|
return agentId;
|
|
}
|
|
|
|
/**
|
|
* Derive the effective user_id from a session key, namespacing per-agent.
|
|
* Falls back to baseUserId when the session is not agent-scoped.
|
|
*/
|
|
export function effectiveUserId(
|
|
baseUserId: string,
|
|
sessionKey?: string,
|
|
): string {
|
|
const agentId = extractAgentId(sessionKey);
|
|
return agentId ? `${baseUserId}:agent:${agentId}` : baseUserId;
|
|
}
|
|
|
|
/** Build a user_id for an explicit agentId (e.g. from tool params). */
|
|
export function agentUserId(baseUserId: string, agentId: string): string {
|
|
return `${baseUserId}:agent:${agentId}`;
|
|
}
|
|
|
|
/**
|
|
* Resolve user_id with priority: explicit agentId > explicit userId > session-derived > configured.
|
|
*/
|
|
export function resolveUserId(
|
|
baseUserId: string,
|
|
opts: { agentId?: string; userId?: string },
|
|
currentSessionId?: string,
|
|
): string {
|
|
if (opts.agentId) return agentUserId(baseUserId, opts.agentId);
|
|
if (opts.userId) return opts.userId;
|
|
return effectiveUserId(baseUserId, currentSessionId);
|
|
}
|