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.
Files changed (213) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +134 -0
  3. package/auth.schema.json +63 -0
  4. package/cron.schema.json +96 -0
  5. package/package.json +72 -0
  6. package/scripts/emit-base-dockerfile.ts +5 -0
  7. package/scripts/generate-schema.ts +34 -0
  8. package/secrets.schema.json +63 -0
  9. package/src/agent/auth.ts +119 -0
  10. package/src/agent/compaction.ts +35 -0
  11. package/src/agent/git-nudge.ts +95 -0
  12. package/src/agent/index.ts +451 -0
  13. package/src/agent/plugin-tools.ts +269 -0
  14. package/src/agent/reload-tool.ts +71 -0
  15. package/src/agent/self.ts +45 -0
  16. package/src/agent/session-origin.ts +288 -0
  17. package/src/agent/subagents.ts +253 -0
  18. package/src/agent/system-prompt.ts +68 -0
  19. package/src/agent/tools/channel-fetch-attachment.ts +118 -0
  20. package/src/agent/tools/channel-history.ts +119 -0
  21. package/src/agent/tools/channel-reply.ts +182 -0
  22. package/src/agent/tools/channel-send.ts +212 -0
  23. package/src/agent/tools/ddg.ts +218 -0
  24. package/src/agent/tools/restart.ts +122 -0
  25. package/src/agent/tools/stream-snapshot.ts +181 -0
  26. package/src/agent/tools/webfetch/fetch.ts +102 -0
  27. package/src/agent/tools/webfetch/index.ts +1 -0
  28. package/src/agent/tools/webfetch/strategies/grep.ts +70 -0
  29. package/src/agent/tools/webfetch/strategies/jq.ts +31 -0
  30. package/src/agent/tools/webfetch/strategies/raw.ts +3 -0
  31. package/src/agent/tools/webfetch/strategies/readability.ts +30 -0
  32. package/src/agent/tools/webfetch/strategies/selector.ts +41 -0
  33. package/src/agent/tools/webfetch/strategies/snapshot.ts +135 -0
  34. package/src/agent/tools/webfetch/tool.ts +281 -0
  35. package/src/agent/tools/webfetch/types.ts +33 -0
  36. package/src/agent/tools/websearch.ts +96 -0
  37. package/src/agent/tools/wikipedia.ts +52 -0
  38. package/src/bundled-plugins/agent-browser/dashboard-discovery.ts +170 -0
  39. package/src/bundled-plugins/agent-browser/dashboard-proxy.ts +421 -0
  40. package/src/bundled-plugins/agent-browser/index.ts +179 -0
  41. package/src/bundled-plugins/agent-browser/shim-install.ts +158 -0
  42. package/src/bundled-plugins/agent-browser/shim.ts +152 -0
  43. package/src/bundled-plugins/agent-browser/skills/agent-browser/SKILL.md +113 -0
  44. package/src/bundled-plugins/guard/index.ts +26 -0
  45. package/src/bundled-plugins/guard/policies/non-workspace-write.ts +98 -0
  46. package/src/bundled-plugins/guard/policies/skill-authoring.ts +185 -0
  47. package/src/bundled-plugins/guard/policies/uncommitted-changes.ts +85 -0
  48. package/src/bundled-plugins/guard/policy.ts +18 -0
  49. package/src/bundled-plugins/memory/README.md +71 -0
  50. package/src/bundled-plugins/memory/append-tool.ts +84 -0
  51. package/src/bundled-plugins/memory/dreaming-state.ts +86 -0
  52. package/src/bundled-plugins/memory/dreaming.ts +470 -0
  53. package/src/bundled-plugins/memory/fragment-parser.ts +67 -0
  54. package/src/bundled-plugins/memory/index.ts +238 -0
  55. package/src/bundled-plugins/memory/load-memory.ts +122 -0
  56. package/src/bundled-plugins/memory/memory-logger.ts +257 -0
  57. package/src/bundled-plugins/memory/secret-detector.ts +49 -0
  58. package/src/bundled-plugins/memory/watermark.ts +15 -0
  59. package/src/bundled-plugins/security/index.ts +35 -0
  60. package/src/bundled-plugins/security/policies/git-exfil.ts +120 -0
  61. package/src/bundled-plugins/security/policies/outbound-secret-scan.ts +167 -0
  62. package/src/bundled-plugins/security/policies/prompt-injection.ts +488 -0
  63. package/src/bundled-plugins/security/policies/secret-exfil-bash.ts +99 -0
  64. package/src/bundled-plugins/security/policies/secret-exfil-read.ts +127 -0
  65. package/src/bundled-plugins/security/policies/session-search-secrets.ts +86 -0
  66. package/src/bundled-plugins/security/policies/ssrf.ts +196 -0
  67. package/src/bundled-plugins/security/policies/system-prompt-leak.ts +81 -0
  68. package/src/bundled-plugins/security/policy.ts +9 -0
  69. package/src/channels/adapters/discord-bot-channel-resolver.ts +77 -0
  70. package/src/channels/adapters/discord-bot-classify.ts +148 -0
  71. package/src/channels/adapters/discord-bot.ts +640 -0
  72. package/src/channels/adapters/kakaotalk-author-resolver.ts +78 -0
  73. package/src/channels/adapters/kakaotalk-channel-resolver.ts +105 -0
  74. package/src/channels/adapters/kakaotalk-classify.ts +77 -0
  75. package/src/channels/adapters/kakaotalk.ts +622 -0
  76. package/src/channels/adapters/slack-bot-author-resolver.ts +80 -0
  77. package/src/channels/adapters/slack-bot-channel-resolver.ts +84 -0
  78. package/src/channels/adapters/slack-bot-classify.ts +213 -0
  79. package/src/channels/adapters/slack-bot-dedupe.ts +51 -0
  80. package/src/channels/adapters/slack-bot-time.ts +10 -0
  81. package/src/channels/adapters/slack-bot.ts +881 -0
  82. package/src/channels/adapters/telegram-bot-classify.ts +155 -0
  83. package/src/channels/adapters/telegram-bot-format.ts +309 -0
  84. package/src/channels/adapters/telegram-bot.ts +604 -0
  85. package/src/channels/engagement.ts +227 -0
  86. package/src/channels/index.ts +21 -0
  87. package/src/channels/manager.ts +292 -0
  88. package/src/channels/membership-cache.ts +116 -0
  89. package/src/channels/membership-from-history.ts +53 -0
  90. package/src/channels/membership.ts +30 -0
  91. package/src/channels/participants.ts +47 -0
  92. package/src/channels/persistence.ts +209 -0
  93. package/src/channels/reloadable.ts +28 -0
  94. package/src/channels/router.ts +1570 -0
  95. package/src/channels/schema.ts +273 -0
  96. package/src/channels/types.ts +160 -0
  97. package/src/cli/channel.ts +403 -0
  98. package/src/cli/compose-status.ts +95 -0
  99. package/src/cli/compose.ts +240 -0
  100. package/src/cli/hostd.ts +163 -0
  101. package/src/cli/index.ts +27 -0
  102. package/src/cli/init.ts +592 -0
  103. package/src/cli/logs.ts +38 -0
  104. package/src/cli/reload.ts +68 -0
  105. package/src/cli/restart.ts +66 -0
  106. package/src/cli/run.ts +77 -0
  107. package/src/cli/shell.ts +33 -0
  108. package/src/cli/start.ts +57 -0
  109. package/src/cli/status.ts +178 -0
  110. package/src/cli/stop.ts +31 -0
  111. package/src/cli/tui.ts +35 -0
  112. package/src/cli/ui.ts +110 -0
  113. package/src/commands/index.ts +74 -0
  114. package/src/compose/discover.ts +43 -0
  115. package/src/compose/index.ts +25 -0
  116. package/src/compose/logs.ts +162 -0
  117. package/src/compose/restart.ts +69 -0
  118. package/src/compose/start.ts +62 -0
  119. package/src/compose/status.ts +28 -0
  120. package/src/compose/stop.ts +43 -0
  121. package/src/config/config.ts +424 -0
  122. package/src/config/index.ts +25 -0
  123. package/src/config/providers.ts +234 -0
  124. package/src/config/reloadable.ts +47 -0
  125. package/src/container/index.ts +27 -0
  126. package/src/container/logs.ts +37 -0
  127. package/src/container/port.ts +137 -0
  128. package/src/container/shared.ts +290 -0
  129. package/src/container/shell.ts +58 -0
  130. package/src/container/start.ts +670 -0
  131. package/src/container/status.ts +76 -0
  132. package/src/container/stop.ts +120 -0
  133. package/src/container/verify-running.ts +149 -0
  134. package/src/cron/consumer.ts +138 -0
  135. package/src/cron/index.ts +54 -0
  136. package/src/cron/reloadable.ts +64 -0
  137. package/src/cron/scheduler.ts +200 -0
  138. package/src/cron/schema.ts +96 -0
  139. package/src/hostd/client.ts +113 -0
  140. package/src/hostd/daemon.ts +587 -0
  141. package/src/hostd/index.ts +25 -0
  142. package/src/hostd/paths.ts +82 -0
  143. package/src/hostd/portbroker-manager.ts +101 -0
  144. package/src/hostd/protocol.ts +48 -0
  145. package/src/hostd/spawn.ts +224 -0
  146. package/src/hostd/supervisor.ts +60 -0
  147. package/src/hostd/tailscale.ts +172 -0
  148. package/src/hostd/version.ts +115 -0
  149. package/src/init/dockerfile.ts +327 -0
  150. package/src/init/ensure-deps.ts +152 -0
  151. package/src/init/gitignore.ts +46 -0
  152. package/src/init/hatching.ts +60 -0
  153. package/src/init/index.ts +786 -0
  154. package/src/init/kakaotalk-auth.ts +114 -0
  155. package/src/init/models-dev.ts +130 -0
  156. package/src/init/oauth-login.ts +74 -0
  157. package/src/init/packagejson.ts +94 -0
  158. package/src/init/paths.ts +2 -0
  159. package/src/init/run-bun-install.ts +20 -0
  160. package/src/markdown/chunk.ts +299 -0
  161. package/src/markdown/index.ts +1 -0
  162. package/src/plugin/context.ts +40 -0
  163. package/src/plugin/define.ts +35 -0
  164. package/src/plugin/hooks.ts +204 -0
  165. package/src/plugin/index.ts +63 -0
  166. package/src/plugin/loader.ts +111 -0
  167. package/src/plugin/manager.ts +136 -0
  168. package/src/plugin/registry.ts +145 -0
  169. package/src/plugin/skills.ts +62 -0
  170. package/src/plugin/types.ts +172 -0
  171. package/src/portbroker/bind-with-forward.ts +102 -0
  172. package/src/portbroker/container-server.ts +305 -0
  173. package/src/portbroker/forward-result-bus.ts +36 -0
  174. package/src/portbroker/hostd-client.ts +443 -0
  175. package/src/portbroker/index.ts +33 -0
  176. package/src/portbroker/policy.ts +24 -0
  177. package/src/portbroker/proc-net-tcp.ts +72 -0
  178. package/src/portbroker/protocol.ts +39 -0
  179. package/src/reload/client.ts +59 -0
  180. package/src/reload/index.ts +3 -0
  181. package/src/reload/registry.ts +60 -0
  182. package/src/reload/types.ts +13 -0
  183. package/src/run/bundled-plugins.ts +24 -0
  184. package/src/run/channel-session-factory.ts +105 -0
  185. package/src/run/index.ts +432 -0
  186. package/src/run/plugin-runtime.ts +43 -0
  187. package/src/run/schema-with-plugins.ts +14 -0
  188. package/src/secrets/index.ts +13 -0
  189. package/src/secrets/migrate.ts +95 -0
  190. package/src/secrets/schema.ts +75 -0
  191. package/src/secrets/storage.ts +231 -0
  192. package/src/server/index.ts +436 -0
  193. package/src/sessions/index.ts +23 -0
  194. package/src/shared/index.ts +9 -0
  195. package/src/shared/local-time.ts +21 -0
  196. package/src/shared/protocol.ts +25 -0
  197. package/src/skills/typeclaw-channel-kakaotalk/SKILL.md +87 -0
  198. package/src/skills/typeclaw-channel-telegram-bot/SKILL.md +64 -0
  199. package/src/skills/typeclaw-config/SKILL.md +643 -0
  200. package/src/skills/typeclaw-cron/SKILL.md +159 -0
  201. package/src/skills/typeclaw-git/SKILL.md +89 -0
  202. package/src/skills/typeclaw-memory/SKILL.md +174 -0
  203. package/src/skills/typeclaw-monorepo/SKILL.md +175 -0
  204. package/src/skills/typeclaw-plugins/SKILL.md +594 -0
  205. package/src/skills/typeclaw-skills/SKILL.md +246 -0
  206. package/src/stream/broker.ts +161 -0
  207. package/src/stream/index.ts +16 -0
  208. package/src/stream/types.ts +69 -0
  209. package/src/tui/client.ts +45 -0
  210. package/src/tui/format.ts +317 -0
  211. package/src/tui/index.ts +225 -0
  212. package/src/tui/theme.ts +41 -0
  213. package/typeclaw.schema.json +826 -0
@@ -0,0 +1,148 @@
1
+ import type {
2
+ DiscordFile,
3
+ DiscordGatewayEmbed,
4
+ DiscordGatewayMessageCreateEvent,
5
+ DiscordGatewayStickerItem,
6
+ } from 'agent-messenger/discordbot'
7
+
8
+ import { isAllowed, type ChannelAdapterConfig } from '@/channels/schema'
9
+ import type { InboundMessage } from '@/channels/types'
10
+
11
+ export type InboundDropReason =
12
+ | 'self_author' // event.author.id === botUserId; we never route our own messages back to ourselves
13
+ | 'empty_content' // SDK delivered content: '' — usually missing MessageContent intent
14
+ | 'not_in_allow_list' // workspace/channel not admitted by typeclaw.json `channels.discord-bot.allow`
15
+ | 'pre_connect' // bot identity is not known yet, so mention/self/reply classification cannot be trusted
16
+
17
+ export type InboundClassification =
18
+ | { kind: 'drop'; reason: InboundDropReason }
19
+ | { kind: 'route'; payload: InboundMessage }
20
+
21
+ // All decision logic for "should this gateway event be routed to the agent?"
22
+ // lives here so it can be unit-tested in isolation. The adapter is left as a
23
+ // thin shell that handles SDK lifecycle and translates this verdict into
24
+ // log lines + router calls. Adding a new drop reason MUST extend
25
+ // InboundDropReason — there is no `default` log path, so the type system
26
+ // forces logging to stay exhaustive.
27
+ export function classifyInbound(
28
+ event: DiscordGatewayMessageCreateEvent,
29
+ config: ChannelAdapterConfig,
30
+ botUserId: string | null,
31
+ ): InboundClassification {
32
+ // Self-drop is the hard floor: we must never route our own messages back to
33
+ // ourselves under any circumstance. We can only do this once botUserId is
34
+ // known (post-connect); before that, fail closed below because mention,
35
+ // reply, and self classification all depend on the bot identity.
36
+ if (botUserId !== null && event.author.id === botUserId) {
37
+ return { kind: 'drop', reason: 'self_author' }
38
+ }
39
+ const text = inboundText(event)
40
+ if (text === '') return { kind: 'drop', reason: 'empty_content' }
41
+
42
+ const isDm = event.guild_id === undefined
43
+ const workspace = isDm ? '@dm' : event.guild_id!
44
+ if (!isAllowed(config.allow, workspace, event.channel_id)) {
45
+ return { kind: 'drop', reason: 'not_in_allow_list' }
46
+ }
47
+
48
+ if (botUserId === null) {
49
+ return { kind: 'drop', reason: 'pre_connect' }
50
+ }
51
+
52
+ // Group mentions (`@everyone`, `@here`, role mentions) are coerced to
53
+ // direct mentions: the broadcast explicitly includes the bot, and the
54
+ // engagement layer doesn't meaningfully distinguish "@bot" from "@channel"
55
+ // — both invite participation. Reusing isBotMention also means the
56
+ // existing 'mention' trigger in typeclaw.json catches both with no new
57
+ // config surface. Discord's gateway already provides structured fields
58
+ // for these, so we don't need to parse content.
59
+ const hasGroupMention = event.mention_everyone === true || (event.mention_roles ?? []).length > 0
60
+ const isBotMention =
61
+ hasGroupMention || event.content.includes(`<@${botUserId}>`) || event.content.includes(`<@!${botUserId}>`)
62
+
63
+ // Discord sends a structured `mentions` array on every message, so we can
64
+ // tell "tagged someone other than us" apart from "no mentions at all"
65
+ // without parsing the content.
66
+ const mentionsOthers = (event.mentions ?? []).length > 0 && !(event.mentions ?? []).some((m) => m.id === botUserId)
67
+
68
+ const replyToParentId = event.message_reference?.message_id
69
+ const replyToBotMessageId = replyToParentId !== undefined && isReplyToBot(event, botUserId) ? replyToParentId : null
70
+ // Discord does not echo the parent message's author on `message_reference`,
71
+ // but in practice the replied-to user is auto-mentioned in the reply's
72
+ // `mentions` array (this is how the Discord client renders the "Replying
73
+ // to @user" header). So when the parent reference exists and our id is NOT
74
+ // among the mentions, the reply is targeted at someone else.
75
+ const replyToOtherMessageId = replyToParentId !== undefined && replyToBotMessageId === null ? replyToParentId : null
76
+
77
+ const ts = Date.parse(event.timestamp)
78
+
79
+ return {
80
+ kind: 'route',
81
+ payload: {
82
+ adapter: 'discord-bot',
83
+ workspace,
84
+ chat: event.channel_id,
85
+ thread: null,
86
+ text,
87
+ externalMessageId: event.id,
88
+ authorId: event.author.id,
89
+ // Discord's post-2023 username system allows pure-numeric handles (e.g.
90
+ // "1411531"); the human-facing display name lives on `global_name`. The
91
+ // history mapper in discord-bot.ts uses the same fallback chain — keep
92
+ // them aligned so the agent sees a stable identity for a given user
93
+ // regardless of whether the message arrived live or via history.
94
+ authorName: event.author.global_name ?? event.author.username,
95
+ authorIsBot: event.author.bot === true,
96
+ isBotMention,
97
+ replyToBotMessageId,
98
+ mentionsOthers,
99
+ replyToOtherMessageId,
100
+ isDm,
101
+ ts: Number.isFinite(ts) ? ts : 0,
102
+ },
103
+ }
104
+ }
105
+
106
+ // Discord's `message_reference.message_id` only carries the parent id, not
107
+ // the parent author. We infer "this reply targets the bot" from the auto-
108
+ // mention Discord injects into the reply's `mentions` array (the same
109
+ // mechanism that drives the "Replying to @user" header in the client).
110
+ function isReplyToBot(event: DiscordGatewayMessageCreateEvent, botUserId: string): boolean {
111
+ return (event.mentions ?? []).some((m) => m.id === botUserId)
112
+ }
113
+
114
+ function inboundText(event: DiscordGatewayMessageCreateEvent): string {
115
+ const mediaSummary = summarizeDiscordMedia(event)
116
+ if (mediaSummary.length === 0) return event.content
117
+ const summary = `[Discord message with ${mediaSummary.join('; ')}]`
118
+ return event.content === '' ? summary : `${event.content}\n${summary}`
119
+ }
120
+
121
+ function summarizeDiscordMedia(event: DiscordGatewayMessageCreateEvent): string[] {
122
+ return [
123
+ ...(event.attachments ?? []).map(summarizeAttachment),
124
+ ...(event.embeds ?? []).map(summarizeEmbed),
125
+ ...(event.sticker_items ?? []).map(summarizeSticker),
126
+ ]
127
+ }
128
+
129
+ function summarizeAttachment(attachment: DiscordFile): string {
130
+ return compactJoin(' ', [
131
+ `attachment: ${attachment.filename}`,
132
+ attachment.content_type === undefined ? undefined : `(${attachment.content_type})`,
133
+ attachment.url,
134
+ ])
135
+ }
136
+
137
+ function summarizeEmbed(embed: DiscordGatewayEmbed): string {
138
+ const label = embed.title ?? embed.description ?? embed.url ?? embed.type ?? 'embed'
139
+ return compactJoin(' ', ['embed:', label, embed.url !== undefined && embed.url !== label ? embed.url : undefined])
140
+ }
141
+
142
+ function summarizeSticker(sticker: DiscordGatewayStickerItem): string {
143
+ return `sticker: ${sticker.name}`
144
+ }
145
+
146
+ function compactJoin(separator: string, parts: Array<string | undefined>): string {
147
+ return parts.filter((part) => part !== undefined && part !== '').join(separator)
148
+ }