typeclaw 0.1.0
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/LICENSE +21 -0
- package/README.md +134 -0
- package/auth.schema.json +63 -0
- package/cron.schema.json +96 -0
- package/package.json +72 -0
- package/scripts/emit-base-dockerfile.ts +5 -0
- package/scripts/generate-schema.ts +34 -0
- package/secrets.schema.json +63 -0
- package/src/agent/auth.ts +119 -0
- package/src/agent/compaction.ts +35 -0
- package/src/agent/git-nudge.ts +95 -0
- package/src/agent/index.ts +451 -0
- package/src/agent/plugin-tools.ts +269 -0
- package/src/agent/reload-tool.ts +71 -0
- package/src/agent/self.ts +45 -0
- package/src/agent/session-origin.ts +288 -0
- package/src/agent/subagents.ts +253 -0
- package/src/agent/system-prompt.ts +68 -0
- package/src/agent/tools/channel-fetch-attachment.ts +118 -0
- package/src/agent/tools/channel-history.ts +119 -0
- package/src/agent/tools/channel-reply.ts +182 -0
- package/src/agent/tools/channel-send.ts +212 -0
- package/src/agent/tools/ddg.ts +218 -0
- package/src/agent/tools/restart.ts +122 -0
- package/src/agent/tools/stream-snapshot.ts +181 -0
- package/src/agent/tools/webfetch/fetch.ts +102 -0
- package/src/agent/tools/webfetch/index.ts +1 -0
- package/src/agent/tools/webfetch/strategies/grep.ts +70 -0
- package/src/agent/tools/webfetch/strategies/jq.ts +31 -0
- package/src/agent/tools/webfetch/strategies/raw.ts +3 -0
- package/src/agent/tools/webfetch/strategies/readability.ts +30 -0
- package/src/agent/tools/webfetch/strategies/selector.ts +41 -0
- package/src/agent/tools/webfetch/strategies/snapshot.ts +135 -0
- package/src/agent/tools/webfetch/tool.ts +281 -0
- package/src/agent/tools/webfetch/types.ts +33 -0
- package/src/agent/tools/websearch.ts +96 -0
- package/src/agent/tools/wikipedia.ts +52 -0
- package/src/bundled-plugins/agent-browser/dashboard-discovery.ts +170 -0
- package/src/bundled-plugins/agent-browser/dashboard-proxy.ts +421 -0
- package/src/bundled-plugins/agent-browser/index.ts +179 -0
- package/src/bundled-plugins/agent-browser/shim-install.ts +158 -0
- package/src/bundled-plugins/agent-browser/shim.ts +152 -0
- package/src/bundled-plugins/agent-browser/skills/agent-browser/SKILL.md +113 -0
- package/src/bundled-plugins/guard/index.ts +26 -0
- package/src/bundled-plugins/guard/policies/non-workspace-write.ts +98 -0
- package/src/bundled-plugins/guard/policies/skill-authoring.ts +185 -0
- package/src/bundled-plugins/guard/policies/uncommitted-changes.ts +85 -0
- package/src/bundled-plugins/guard/policy.ts +18 -0
- package/src/bundled-plugins/memory/README.md +71 -0
- package/src/bundled-plugins/memory/append-tool.ts +84 -0
- package/src/bundled-plugins/memory/dreaming-state.ts +86 -0
- package/src/bundled-plugins/memory/dreaming.ts +470 -0
- package/src/bundled-plugins/memory/fragment-parser.ts +67 -0
- package/src/bundled-plugins/memory/index.ts +238 -0
- package/src/bundled-plugins/memory/load-memory.ts +122 -0
- package/src/bundled-plugins/memory/memory-logger.ts +257 -0
- package/src/bundled-plugins/memory/secret-detector.ts +49 -0
- package/src/bundled-plugins/memory/watermark.ts +15 -0
- package/src/bundled-plugins/security/index.ts +35 -0
- package/src/bundled-plugins/security/policies/git-exfil.ts +120 -0
- package/src/bundled-plugins/security/policies/outbound-secret-scan.ts +167 -0
- package/src/bundled-plugins/security/policies/prompt-injection.ts +488 -0
- package/src/bundled-plugins/security/policies/secret-exfil-bash.ts +99 -0
- package/src/bundled-plugins/security/policies/secret-exfil-read.ts +127 -0
- package/src/bundled-plugins/security/policies/session-search-secrets.ts +86 -0
- package/src/bundled-plugins/security/policies/ssrf.ts +196 -0
- package/src/bundled-plugins/security/policies/system-prompt-leak.ts +81 -0
- package/src/bundled-plugins/security/policy.ts +9 -0
- package/src/channels/adapters/discord-bot-channel-resolver.ts +77 -0
- package/src/channels/adapters/discord-bot-classify.ts +148 -0
- package/src/channels/adapters/discord-bot.ts +640 -0
- package/src/channels/adapters/kakaotalk-author-resolver.ts +78 -0
- package/src/channels/adapters/kakaotalk-channel-resolver.ts +105 -0
- package/src/channels/adapters/kakaotalk-classify.ts +77 -0
- package/src/channels/adapters/kakaotalk.ts +622 -0
- package/src/channels/adapters/slack-bot-author-resolver.ts +80 -0
- package/src/channels/adapters/slack-bot-channel-resolver.ts +84 -0
- package/src/channels/adapters/slack-bot-classify.ts +213 -0
- package/src/channels/adapters/slack-bot-dedupe.ts +51 -0
- package/src/channels/adapters/slack-bot-time.ts +10 -0
- package/src/channels/adapters/slack-bot.ts +881 -0
- package/src/channels/adapters/telegram-bot-classify.ts +155 -0
- package/src/channels/adapters/telegram-bot-format.ts +309 -0
- package/src/channels/adapters/telegram-bot.ts +604 -0
- package/src/channels/engagement.ts +227 -0
- package/src/channels/index.ts +21 -0
- package/src/channels/manager.ts +292 -0
- package/src/channels/membership-cache.ts +116 -0
- package/src/channels/membership-from-history.ts +53 -0
- package/src/channels/membership.ts +30 -0
- package/src/channels/participants.ts +47 -0
- package/src/channels/persistence.ts +209 -0
- package/src/channels/reloadable.ts +28 -0
- package/src/channels/router.ts +1570 -0
- package/src/channels/schema.ts +273 -0
- package/src/channels/types.ts +160 -0
- package/src/cli/channel.ts +403 -0
- package/src/cli/compose-status.ts +95 -0
- package/src/cli/compose.ts +240 -0
- package/src/cli/hostd.ts +163 -0
- package/src/cli/index.ts +27 -0
- package/src/cli/init.ts +592 -0
- package/src/cli/logs.ts +38 -0
- package/src/cli/reload.ts +68 -0
- package/src/cli/restart.ts +66 -0
- package/src/cli/run.ts +77 -0
- package/src/cli/shell.ts +33 -0
- package/src/cli/start.ts +57 -0
- package/src/cli/status.ts +178 -0
- package/src/cli/stop.ts +31 -0
- package/src/cli/tui.ts +35 -0
- package/src/cli/ui.ts +110 -0
- package/src/commands/index.ts +74 -0
- package/src/compose/discover.ts +43 -0
- package/src/compose/index.ts +25 -0
- package/src/compose/logs.ts +162 -0
- package/src/compose/restart.ts +69 -0
- package/src/compose/start.ts +62 -0
- package/src/compose/status.ts +28 -0
- package/src/compose/stop.ts +43 -0
- package/src/config/config.ts +424 -0
- package/src/config/index.ts +25 -0
- package/src/config/providers.ts +234 -0
- package/src/config/reloadable.ts +47 -0
- package/src/container/index.ts +27 -0
- package/src/container/logs.ts +37 -0
- package/src/container/port.ts +137 -0
- package/src/container/shared.ts +290 -0
- package/src/container/shell.ts +58 -0
- package/src/container/start.ts +670 -0
- package/src/container/status.ts +76 -0
- package/src/container/stop.ts +120 -0
- package/src/container/verify-running.ts +149 -0
- package/src/cron/consumer.ts +138 -0
- package/src/cron/index.ts +54 -0
- package/src/cron/reloadable.ts +64 -0
- package/src/cron/scheduler.ts +200 -0
- package/src/cron/schema.ts +96 -0
- package/src/hostd/client.ts +113 -0
- package/src/hostd/daemon.ts +587 -0
- package/src/hostd/index.ts +25 -0
- package/src/hostd/paths.ts +82 -0
- package/src/hostd/portbroker-manager.ts +101 -0
- package/src/hostd/protocol.ts +48 -0
- package/src/hostd/spawn.ts +224 -0
- package/src/hostd/supervisor.ts +60 -0
- package/src/hostd/tailscale.ts +172 -0
- package/src/hostd/version.ts +115 -0
- package/src/init/dockerfile.ts +327 -0
- package/src/init/ensure-deps.ts +152 -0
- package/src/init/gitignore.ts +46 -0
- package/src/init/hatching.ts +60 -0
- package/src/init/index.ts +786 -0
- package/src/init/kakaotalk-auth.ts +114 -0
- package/src/init/models-dev.ts +130 -0
- package/src/init/oauth-login.ts +74 -0
- package/src/init/packagejson.ts +94 -0
- package/src/init/paths.ts +2 -0
- package/src/init/run-bun-install.ts +20 -0
- package/src/markdown/chunk.ts +299 -0
- package/src/markdown/index.ts +1 -0
- package/src/plugin/context.ts +40 -0
- package/src/plugin/define.ts +35 -0
- package/src/plugin/hooks.ts +204 -0
- package/src/plugin/index.ts +63 -0
- package/src/plugin/loader.ts +111 -0
- package/src/plugin/manager.ts +136 -0
- package/src/plugin/registry.ts +145 -0
- package/src/plugin/skills.ts +62 -0
- package/src/plugin/types.ts +172 -0
- package/src/portbroker/bind-with-forward.ts +102 -0
- package/src/portbroker/container-server.ts +305 -0
- package/src/portbroker/forward-result-bus.ts +36 -0
- package/src/portbroker/hostd-client.ts +443 -0
- package/src/portbroker/index.ts +33 -0
- package/src/portbroker/policy.ts +24 -0
- package/src/portbroker/proc-net-tcp.ts +72 -0
- package/src/portbroker/protocol.ts +39 -0
- package/src/reload/client.ts +59 -0
- package/src/reload/index.ts +3 -0
- package/src/reload/registry.ts +60 -0
- package/src/reload/types.ts +13 -0
- package/src/run/bundled-plugins.ts +24 -0
- package/src/run/channel-session-factory.ts +105 -0
- package/src/run/index.ts +432 -0
- package/src/run/plugin-runtime.ts +43 -0
- package/src/run/schema-with-plugins.ts +14 -0
- package/src/secrets/index.ts +13 -0
- package/src/secrets/migrate.ts +95 -0
- package/src/secrets/schema.ts +75 -0
- package/src/secrets/storage.ts +231 -0
- package/src/server/index.ts +436 -0
- package/src/sessions/index.ts +23 -0
- package/src/shared/index.ts +9 -0
- package/src/shared/local-time.ts +21 -0
- package/src/shared/protocol.ts +25 -0
- package/src/skills/typeclaw-channel-kakaotalk/SKILL.md +87 -0
- package/src/skills/typeclaw-channel-telegram-bot/SKILL.md +64 -0
- package/src/skills/typeclaw-config/SKILL.md +643 -0
- package/src/skills/typeclaw-cron/SKILL.md +159 -0
- package/src/skills/typeclaw-git/SKILL.md +89 -0
- package/src/skills/typeclaw-memory/SKILL.md +174 -0
- package/src/skills/typeclaw-monorepo/SKILL.md +175 -0
- package/src/skills/typeclaw-plugins/SKILL.md +594 -0
- package/src/skills/typeclaw-skills/SKILL.md +246 -0
- package/src/stream/broker.ts +161 -0
- package/src/stream/index.ts +16 -0
- package/src/stream/types.ts +69 -0
- package/src/tui/client.ts +45 -0
- package/src/tui/format.ts +317 -0
- package/src/tui/index.ts +225 -0
- package/src/tui/theme.ts +41 -0
- package/typeclaw.schema.json +826 -0
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import type { KakaoTalkClient } from './kakaotalk'
|
|
2
|
+
|
|
3
|
+
const DEFAULT_TTL_MS = 5 * 60 * 1000
|
|
4
|
+
|
|
5
|
+
export type KakaoAuthorResolver = {
|
|
6
|
+
resolve: (authorId: string, chatId: string) => Promise<string | null>
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export type KakaoAuthorResolverOptions = {
|
|
10
|
+
client: Pick<KakaoTalkClient, 'getMembers'>
|
|
11
|
+
now?: () => number
|
|
12
|
+
ttlMs?: number
|
|
13
|
+
logger?: { warn: (msg: string) => void }
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
type ChatCacheEntry = {
|
|
17
|
+
members: Map<string, string>
|
|
18
|
+
expiresAt: number
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// Resolves author IDs to nicknames via LOCO GETMEM (one call per chat per
|
|
22
|
+
// TTL window). Only invoked when the inline `author_name` from the chat-
|
|
23
|
+
// list snapshot is missing — the agent-messenger client already covers
|
|
24
|
+
// "display members" (~5 per chat) for free, and this resolver fills the
|
|
25
|
+
// gap for larger groups and open chats.
|
|
26
|
+
//
|
|
27
|
+
// Lazy by design: a chat's member list is fetched on the first lookup
|
|
28
|
+
// miss, not eagerly on connect. Most agent folders watch a small handful
|
|
29
|
+
// of chats; prefetching every chat's member list at startup would burn
|
|
30
|
+
// LOCO calls on rooms the agent never sees a message in.
|
|
31
|
+
export function createKakaoAuthorResolver(options: KakaoAuthorResolverOptions): KakaoAuthorResolver {
|
|
32
|
+
const now = options.now ?? Date.now
|
|
33
|
+
const ttlMs = options.ttlMs ?? DEFAULT_TTL_MS
|
|
34
|
+
const logger = options.logger
|
|
35
|
+
const cache = new Map<string, ChatCacheEntry>()
|
|
36
|
+
const inflight = new Map<string, Promise<ChatCacheEntry | null>>()
|
|
37
|
+
|
|
38
|
+
const fetchMembers = (chatId: string): Promise<ChatCacheEntry | null> => {
|
|
39
|
+
const existing = inflight.get(chatId)
|
|
40
|
+
if (existing !== undefined) return existing
|
|
41
|
+
const promise = options.client
|
|
42
|
+
.getMembers(chatId)
|
|
43
|
+
.then((members): ChatCacheEntry => {
|
|
44
|
+
const map = new Map<string, string>()
|
|
45
|
+
for (const m of members) map.set(m.user_id, m.nickname)
|
|
46
|
+
const entry: ChatCacheEntry = { members: map, expiresAt: now() + ttlMs }
|
|
47
|
+
cache.set(chatId, entry)
|
|
48
|
+
return entry
|
|
49
|
+
})
|
|
50
|
+
.catch((err: unknown): null => {
|
|
51
|
+
// Author resolution is best-effort. Failing here makes the message
|
|
52
|
+
// render with the raw user_id, which is uglier but still routes.
|
|
53
|
+
logger?.warn(`[kakaotalk] getMembers(${chatId}) failed: ${describe(err)}`)
|
|
54
|
+
return null
|
|
55
|
+
})
|
|
56
|
+
.finally(() => {
|
|
57
|
+
inflight.delete(chatId)
|
|
58
|
+
})
|
|
59
|
+
inflight.set(chatId, promise)
|
|
60
|
+
return promise
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const resolve = async (authorId: string, chatId: string): Promise<string | null> => {
|
|
64
|
+
const cached = cache.get(chatId)
|
|
65
|
+
if (cached !== undefined && cached.expiresAt > now()) {
|
|
66
|
+
return cached.members.get(authorId) ?? null
|
|
67
|
+
}
|
|
68
|
+
const fresh = await fetchMembers(chatId)
|
|
69
|
+
if (fresh === null) return null
|
|
70
|
+
return fresh.members.get(authorId) ?? null
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return { resolve }
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function describe(err: unknown): string {
|
|
77
|
+
return err instanceof Error ? err.message : String(err)
|
|
78
|
+
}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { classifyKakaoChat, type KakaoChat, type KakaoChatKind, type KakaoTalkClient } from 'agent-messenger/kakaotalk'
|
|
2
|
+
|
|
3
|
+
import type { ChannelKey, ChannelNameResolver, ResolvedChannelNames } from '@/channels/types'
|
|
4
|
+
|
|
5
|
+
const DEFAULT_TTL_MS = 5 * 60 * 1000
|
|
6
|
+
|
|
7
|
+
export type KakaoWorkspace = '@kakao-dm' | '@kakao-group' | '@kakao-open'
|
|
8
|
+
|
|
9
|
+
export function kakaoWorkspaceForType(kind: KakaoChatKind): KakaoWorkspace {
|
|
10
|
+
if (kind === 'dm') return '@kakao-dm'
|
|
11
|
+
if (kind === 'open') return '@kakao-open'
|
|
12
|
+
return '@kakao-group'
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export type KakaoChatLookupValue = {
|
|
16
|
+
workspace: KakaoWorkspace
|
|
17
|
+
isDm: boolean
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export type KakaoChannelResolver = {
|
|
21
|
+
resolve: ChannelNameResolver
|
|
22
|
+
lookupChat: (chatId: string) => KakaoChatLookupValue | null
|
|
23
|
+
refresh: () => Promise<void>
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export type KakaoChannelResolverOptions = {
|
|
27
|
+
client: Pick<KakaoTalkClient, 'getChats'>
|
|
28
|
+
now?: () => number
|
|
29
|
+
ttlMs?: number
|
|
30
|
+
logger?: { warn: (msg: string) => void }
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
type Entry = {
|
|
34
|
+
workspace: KakaoWorkspace
|
|
35
|
+
isDm: boolean
|
|
36
|
+
chatName: string | null
|
|
37
|
+
expiresAt: number
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function createKakaoChannelResolver(options: KakaoChannelResolverOptions): KakaoChannelResolver {
|
|
41
|
+
const now = options.now ?? Date.now
|
|
42
|
+
const ttlMs = options.ttlMs ?? DEFAULT_TTL_MS
|
|
43
|
+
const cache = new Map<string, Entry>()
|
|
44
|
+
let inflight: Promise<void> | null = null
|
|
45
|
+
|
|
46
|
+
const refresh = async (): Promise<void> => {
|
|
47
|
+
if (inflight !== null) {
|
|
48
|
+
await inflight
|
|
49
|
+
return
|
|
50
|
+
}
|
|
51
|
+
const promise = loadAll().finally(() => {
|
|
52
|
+
inflight = null
|
|
53
|
+
})
|
|
54
|
+
inflight = promise
|
|
55
|
+
await promise
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const loadAll = async (): Promise<void> => {
|
|
59
|
+
try {
|
|
60
|
+
const chats = await options.client.getChats({ all: true })
|
|
61
|
+
const expiresAt = now() + ttlMs
|
|
62
|
+
for (const chat of chats) ingest(chat, expiresAt)
|
|
63
|
+
} catch (err) {
|
|
64
|
+
options.logger?.warn(`[kakaotalk] channel resolver refresh failed: ${describe(err)}`)
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const ingest = (chat: KakaoChat, expiresAt: number): void => {
|
|
69
|
+
const kind = classifyKakaoChat(chat)
|
|
70
|
+
const workspace = kakaoWorkspaceForType(kind)
|
|
71
|
+
cache.set(chat.chat_id, {
|
|
72
|
+
workspace,
|
|
73
|
+
isDm: kind === 'dm',
|
|
74
|
+
chatName: chat.title ?? chat.display_name,
|
|
75
|
+
expiresAt,
|
|
76
|
+
})
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const resolve: ChannelNameResolver = async (key: ChannelKey): Promise<ResolvedChannelNames> => {
|
|
80
|
+
const entry = cache.get(key.chat)
|
|
81
|
+
if (entry === undefined || entry.expiresAt <= now()) await refresh()
|
|
82
|
+
const fresh = cache.get(key.chat)
|
|
83
|
+
if (fresh === undefined) return {}
|
|
84
|
+
const result: ResolvedChannelNames = {}
|
|
85
|
+
if (fresh.chatName !== null && fresh.chatName !== '') result.chatName = fresh.chatName
|
|
86
|
+
return result
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// Sync lookup. Returns null when the entry is missing OR stale; callers
|
|
90
|
+
// (e.g. inbound classification, history allow checks) MUST treat null as
|
|
91
|
+
// "refresh needed", not "unknown forever". The classifier handles this
|
|
92
|
+
// by awaiting `refresh()` and re-checking before dropping the message —
|
|
93
|
+
// see kakaotalk.ts handleMessageEvent.
|
|
94
|
+
const lookupChat = (chatId: string): KakaoChatLookupValue | null => {
|
|
95
|
+
const entry = cache.get(chatId)
|
|
96
|
+
if (entry === undefined || entry.expiresAt <= now()) return null
|
|
97
|
+
return { workspace: entry.workspace, isDm: entry.isDm }
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return { resolve, lookupChat, refresh }
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function describe(err: unknown): string {
|
|
104
|
+
return err instanceof Error ? err.message : String(err)
|
|
105
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import type { KakaoTalkPushMessageEvent } from 'agent-messenger/kakaotalk'
|
|
2
|
+
|
|
3
|
+
import { matchesAnyAlias } from '@/channels/engagement'
|
|
4
|
+
import { isAllowed, type ChannelAdapterConfig } from '@/channels/schema'
|
|
5
|
+
import type { InboundMessage } from '@/channels/types'
|
|
6
|
+
|
|
7
|
+
export type InboundDropReason = 'self_author' | 'empty_text' | 'unknown_chat' | 'not_in_allow_list' | 'pre_connect'
|
|
8
|
+
|
|
9
|
+
export type InboundClassification =
|
|
10
|
+
| { kind: 'drop'; reason: InboundDropReason }
|
|
11
|
+
| { kind: 'route'; payload: InboundMessage }
|
|
12
|
+
|
|
13
|
+
export type KakaoChatLookup = (chatId: string) => {
|
|
14
|
+
workspace: '@kakao-dm' | '@kakao-group' | '@kakao-open'
|
|
15
|
+
isDm: boolean
|
|
16
|
+
} | null
|
|
17
|
+
|
|
18
|
+
export type KakaoInboundContext = {
|
|
19
|
+
selfUserId: string | null
|
|
20
|
+
lookupChat: KakaoChatLookup
|
|
21
|
+
selfAliases?: readonly string[]
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function classifyInbound(
|
|
25
|
+
event: KakaoTalkPushMessageEvent,
|
|
26
|
+
config: ChannelAdapterConfig,
|
|
27
|
+
context: KakaoInboundContext,
|
|
28
|
+
): InboundClassification {
|
|
29
|
+
if (context.selfUserId === null) {
|
|
30
|
+
return { kind: 'drop', reason: 'pre_connect' }
|
|
31
|
+
}
|
|
32
|
+
if (String(event.author_id) === context.selfUserId) {
|
|
33
|
+
return { kind: 'drop', reason: 'self_author' }
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const text = event.message ?? ''
|
|
37
|
+
if (text === '') return { kind: 'drop', reason: 'empty_text' }
|
|
38
|
+
|
|
39
|
+
const chatInfo = context.lookupChat(event.chat_id)
|
|
40
|
+
if (chatInfo === null) {
|
|
41
|
+
return { kind: 'drop', reason: 'unknown_chat' }
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (!isAllowed(config.allow, chatInfo.workspace, event.chat_id)) {
|
|
45
|
+
return { kind: 'drop', reason: 'not_in_allow_list' }
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// KakaoTalk has no native @-mention syntax in the LOCO protocol that the
|
|
49
|
+
// SDK exposes (mention rendering happens client-side via display_name
|
|
50
|
+
// matching). Mention-equivalent engagement comes solely from alias
|
|
51
|
+
// matching, which the engagement layer treats as equivalent to a direct
|
|
52
|
+
// mention (see engagement.ts: alias is unconditional and ranks alongside
|
|
53
|
+
// explicit triggers). Without aliases configured, only `reply` and `dm`
|
|
54
|
+
// triggers can fire on KakaoTalk.
|
|
55
|
+
const aliasMatched = matchesAnyAlias(text, context.selfAliases ?? [])
|
|
56
|
+
|
|
57
|
+
return {
|
|
58
|
+
kind: 'route',
|
|
59
|
+
payload: {
|
|
60
|
+
adapter: 'kakaotalk',
|
|
61
|
+
workspace: chatInfo.workspace,
|
|
62
|
+
chat: event.chat_id,
|
|
63
|
+
thread: null,
|
|
64
|
+
text,
|
|
65
|
+
externalMessageId: event.log_id,
|
|
66
|
+
authorId: String(event.author_id),
|
|
67
|
+
authorName: event.author_name ?? String(event.author_id),
|
|
68
|
+
authorIsBot: false,
|
|
69
|
+
isBotMention: aliasMatched,
|
|
70
|
+
replyToBotMessageId: null,
|
|
71
|
+
mentionsOthers: false,
|
|
72
|
+
replyToOtherMessageId: null,
|
|
73
|
+
isDm: chatInfo.isDm,
|
|
74
|
+
ts: event.sent_at,
|
|
75
|
+
},
|
|
76
|
+
}
|
|
77
|
+
}
|