godcrm/utils/invocationTokens.ts
GOD CRM Release f89e074dd1
Some checks failed
CI / Lint / Typecheck / Test / Build (push) Has been cancelled
CI / PostgreSQL Integration Tests (push) Has been cancelled
GOD CRM — public scrubbed snapshot
Governed substrate for autonomous agents: scoped identity (passports),
audited actions, MCP workspace. Infra IPs and secrets redacted for public release.
2026-08-10 04:01:45 +03:00

66 lines
1.9 KiB
TypeScript

/**
* ADR-116: Structured Invocation Token Utilities
*
* validateAndWrapMentions — wraps bare @slug in <<@slug>> if slug is a known user
* validateAndWrapCommands — wraps bare /slug in <</slug>> if slug is a known agent
*
* Both functions are called on submit, just before sending the message content
* to the backend, so that the backend always receives structured tokens.
*/
import type { MentionUser } from '../components/MentionInput';
/**
* Task 11: Wrap bare @mentions with structured invocation tokens.
*
* Converts `@slug` to `<<@slug>>` when the slug matches a known user,
* but only if the mention is not already wrapped (negative lookbehind for `<<`).
*/
export function validateAndWrapMentions(
content: string,
availableUsers: MentionUser[]
): string {
const slugSet = new Set(
availableUsers.map((u) =>
u.name.toLowerCase().replace(/[^a-z0-9]+/g, '-')
)
);
return content.replace(
/(?<!<<)@([a-z0-9_-]+)/gi,
(match, slug) => {
return slugSet.has(slug.toLowerCase())
? `<<@${slug.toLowerCase()}>>`
: match;
}
);
}
/**
* Task 12: Wrap bare /commands with structured invocation tokens.
*
* Converts `/slug` to `<</slug>>` when the slug matches a known agent,
* but only if the command is not already wrapped (negative lookbehind for `<<`).
* Only agents (user_type === 'agent') are considered valid command targets.
*/
export function validateAndWrapCommands(
content: string,
availableAgents: MentionUser[]
): string {
const slugSet = new Set(
availableAgents
.filter((u) => u.type === 'agent')
.map((u) =>
u.name.toLowerCase().replace(/[^a-z0-9]+/g, '-')
)
);
return content.replace(
/(?<!<<)(?:^|\s)\/(([a-z][a-z0-9_-]*))(?=\s|$)/gim,
(match, slug) => {
return slugSet.has(slug.toLowerCase())
? match.replace(`/${slug}`, `<</${slug.toLowerCase()}>>`)
: match;
}
);
}