@brianli/kimaki 0.4.72-brianli.1

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 (328) hide show
  1. package/bin.js +2 -0
  2. package/dist/ai-tool-to-genai.js +233 -0
  3. package/dist/ai-tool-to-genai.test.js +267 -0
  4. package/dist/ai-tool.js +6 -0
  5. package/dist/bin.js +87 -0
  6. package/dist/bot-token.js +121 -0
  7. package/dist/bot-token.test.js +134 -0
  8. package/dist/channel-management.js +101 -0
  9. package/dist/cli-parsing.test.js +89 -0
  10. package/dist/cli.js +2529 -0
  11. package/dist/commands/abort.js +82 -0
  12. package/dist/commands/action-buttons.js +257 -0
  13. package/dist/commands/add-project.js +114 -0
  14. package/dist/commands/agent.js +291 -0
  15. package/dist/commands/ask-question.js +223 -0
  16. package/dist/commands/compact.js +120 -0
  17. package/dist/commands/context-usage.js +140 -0
  18. package/dist/commands/create-new-project.js +118 -0
  19. package/dist/commands/diff.js +128 -0
  20. package/dist/commands/file-upload.js +275 -0
  21. package/dist/commands/fork.js +217 -0
  22. package/dist/commands/gemini-apikey.js +70 -0
  23. package/dist/commands/login.js +490 -0
  24. package/dist/commands/mention-mode.js +51 -0
  25. package/dist/commands/merge-worktree.js +124 -0
  26. package/dist/commands/model.js +694 -0
  27. package/dist/commands/permissions.js +163 -0
  28. package/dist/commands/queue.js +217 -0
  29. package/dist/commands/remove-project.js +115 -0
  30. package/dist/commands/restart-opencode-server.js +116 -0
  31. package/dist/commands/resume.js +159 -0
  32. package/dist/commands/run-command.js +79 -0
  33. package/dist/commands/session-id.js +78 -0
  34. package/dist/commands/session.js +192 -0
  35. package/dist/commands/share.js +80 -0
  36. package/dist/commands/types.js +2 -0
  37. package/dist/commands/undo-redo.js +159 -0
  38. package/dist/commands/unset-model.js +152 -0
  39. package/dist/commands/upgrade.js +42 -0
  40. package/dist/commands/user-command.js +148 -0
  41. package/dist/commands/verbosity.js +60 -0
  42. package/dist/commands/worktree-settings.js +50 -0
  43. package/dist/commands/worktree.js +299 -0
  44. package/dist/condense-memory.js +33 -0
  45. package/dist/config.js +110 -0
  46. package/dist/database.js +1050 -0
  47. package/dist/db.js +159 -0
  48. package/dist/db.test.js +49 -0
  49. package/dist/discord-api.js +28 -0
  50. package/dist/discord-auth.js +231 -0
  51. package/dist/discord-auth.test.js +80 -0
  52. package/dist/discord-bot.js +997 -0
  53. package/dist/discord-utils.js +560 -0
  54. package/dist/discord-utils.test.js +115 -0
  55. package/dist/errors.js +167 -0
  56. package/dist/escape-backticks.test.js +429 -0
  57. package/dist/format-tables.js +122 -0
  58. package/dist/format-tables.test.js +199 -0
  59. package/dist/forum-sync/config.js +79 -0
  60. package/dist/forum-sync/discord-operations.js +154 -0
  61. package/dist/forum-sync/index.js +5 -0
  62. package/dist/forum-sync/markdown.js +117 -0
  63. package/dist/forum-sync/sync-to-discord.js +417 -0
  64. package/dist/forum-sync/sync-to-files.js +190 -0
  65. package/dist/forum-sync/types.js +53 -0
  66. package/dist/forum-sync/watchers.js +307 -0
  67. package/dist/gateway-consumer.js +232 -0
  68. package/dist/gateway-consumer.test.js +18 -0
  69. package/dist/genai-worker-wrapper.js +111 -0
  70. package/dist/genai-worker.js +311 -0
  71. package/dist/genai.js +232 -0
  72. package/dist/generated/browser.js +17 -0
  73. package/dist/generated/client.js +35 -0
  74. package/dist/generated/commonInputTypes.js +10 -0
  75. package/dist/generated/enums.js +30 -0
  76. package/dist/generated/internal/class.js +41 -0
  77. package/dist/generated/internal/prismaNamespace.js +239 -0
  78. package/dist/generated/internal/prismaNamespaceBrowser.js +209 -0
  79. package/dist/generated/models/bot_api_keys.js +1 -0
  80. package/dist/generated/models/bot_tokens.js +1 -0
  81. package/dist/generated/models/channel_agents.js +1 -0
  82. package/dist/generated/models/channel_directories.js +1 -0
  83. package/dist/generated/models/channel_mention_mode.js +1 -0
  84. package/dist/generated/models/channel_models.js +1 -0
  85. package/dist/generated/models/channel_verbosity.js +1 -0
  86. package/dist/generated/models/channel_worktrees.js +1 -0
  87. package/dist/generated/models/forum_sync_configs.js +1 -0
  88. package/dist/generated/models/global_models.js +1 -0
  89. package/dist/generated/models/ipc_requests.js +1 -0
  90. package/dist/generated/models/part_messages.js +1 -0
  91. package/dist/generated/models/scheduled_tasks.js +1 -0
  92. package/dist/generated/models/session_agents.js +1 -0
  93. package/dist/generated/models/session_models.js +1 -0
  94. package/dist/generated/models/session_start_sources.js +1 -0
  95. package/dist/generated/models/thread_sessions.js +1 -0
  96. package/dist/generated/models/thread_worktrees.js +1 -0
  97. package/dist/generated/models.js +1 -0
  98. package/dist/heap-monitor.js +95 -0
  99. package/dist/hrana-server.js +416 -0
  100. package/dist/hrana-server.test.js +368 -0
  101. package/dist/image-utils.js +112 -0
  102. package/dist/interaction-handler.js +327 -0
  103. package/dist/ipc-polling.js +251 -0
  104. package/dist/kimaki-digital-twin.e2e.test.js +165 -0
  105. package/dist/limit-heading-depth.js +25 -0
  106. package/dist/limit-heading-depth.test.js +105 -0
  107. package/dist/logger.js +160 -0
  108. package/dist/markdown.js +342 -0
  109. package/dist/markdown.test.js +253 -0
  110. package/dist/message-formatting.js +433 -0
  111. package/dist/message-formatting.test.js +73 -0
  112. package/dist/openai-realtime.js +228 -0
  113. package/dist/opencode-plugin-loading.e2e.test.js +91 -0
  114. package/dist/opencode-plugin.js +536 -0
  115. package/dist/opencode-plugin.test.js +98 -0
  116. package/dist/opencode.js +409 -0
  117. package/dist/privacy-sanitizer.js +105 -0
  118. package/dist/runtime-mode.js +51 -0
  119. package/dist/runtime-mode.test.js +115 -0
  120. package/dist/sentry.js +127 -0
  121. package/dist/session-handler/state.js +151 -0
  122. package/dist/session-handler.js +1874 -0
  123. package/dist/session-search.js +100 -0
  124. package/dist/session-search.test.js +40 -0
  125. package/dist/startup-service.js +153 -0
  126. package/dist/system-message.js +499 -0
  127. package/dist/task-runner.js +282 -0
  128. package/dist/task-schedule.js +191 -0
  129. package/dist/task-schedule.test.js +71 -0
  130. package/dist/thinking-utils.js +35 -0
  131. package/dist/thread-message-queue.e2e.test.js +781 -0
  132. package/dist/tools.js +359 -0
  133. package/dist/unnest-code-blocks.js +136 -0
  134. package/dist/unnest-code-blocks.test.js +641 -0
  135. package/dist/upgrade.js +114 -0
  136. package/dist/utils.js +109 -0
  137. package/dist/voice-handler.js +606 -0
  138. package/dist/voice.js +304 -0
  139. package/dist/voice.test.js +187 -0
  140. package/dist/wait-session.js +94 -0
  141. package/dist/worker-types.js +4 -0
  142. package/dist/worktree-utils.js +727 -0
  143. package/dist/xml.js +92 -0
  144. package/dist/xml.test.js +32 -0
  145. package/package.json +82 -0
  146. package/schema.prisma +246 -0
  147. package/skills/batch/SKILL.md +87 -0
  148. package/skills/critique/SKILL.md +129 -0
  149. package/skills/errore/SKILL.md +589 -0
  150. package/skills/goke/.prettierrc +5 -0
  151. package/skills/goke/CHANGELOG.md +40 -0
  152. package/skills/goke/LICENSE +21 -0
  153. package/skills/goke/README.md +666 -0
  154. package/skills/goke/SKILL.md +458 -0
  155. package/skills/goke/package.json +43 -0
  156. package/skills/goke/src/__test__/coerce.test.ts +411 -0
  157. package/skills/goke/src/__test__/index.test.ts +1798 -0
  158. package/skills/goke/src/__test__/types.test-d.ts +111 -0
  159. package/skills/goke/src/coerce.ts +547 -0
  160. package/skills/goke/src/goke.ts +1362 -0
  161. package/skills/goke/src/index.ts +16 -0
  162. package/skills/goke/src/mri.ts +164 -0
  163. package/skills/goke/tsconfig.json +15 -0
  164. package/skills/jitter/EDITOR.md +219 -0
  165. package/skills/jitter/EXPORT-INTERNALS.md +309 -0
  166. package/skills/jitter/SKILL.md +158 -0
  167. package/skills/jitter/jitter-clipboard.json +1042 -0
  168. package/skills/jitter/package.json +14 -0
  169. package/skills/jitter/tsconfig.json +15 -0
  170. package/skills/jitter/utils/actions.ts +212 -0
  171. package/skills/jitter/utils/export.ts +114 -0
  172. package/skills/jitter/utils/index.ts +141 -0
  173. package/skills/jitter/utils/snapshot.ts +154 -0
  174. package/skills/jitter/utils/traverse.ts +246 -0
  175. package/skills/jitter/utils/types.ts +279 -0
  176. package/skills/jitter/utils/wait.ts +133 -0
  177. package/skills/playwriter/SKILL.md +31 -0
  178. package/skills/security-review/SKILL.md +208 -0
  179. package/skills/simplify/SKILL.md +58 -0
  180. package/skills/termcast/SKILL.md +945 -0
  181. package/skills/tuistory/SKILL.md +250 -0
  182. package/skills/zustand-centralized-state/SKILL.md +582 -0
  183. package/src/__snapshots__/compact-session-context-no-system.md +35 -0
  184. package/src/__snapshots__/compact-session-context.md +41 -0
  185. package/src/__snapshots__/first-session-no-info.md +17 -0
  186. package/src/__snapshots__/first-session-with-info.md +23 -0
  187. package/src/__snapshots__/session-1.md +17 -0
  188. package/src/__snapshots__/session-2.md +5871 -0
  189. package/src/__snapshots__/session-3.md +17 -0
  190. package/src/__snapshots__/session-with-tools.md +5871 -0
  191. package/src/ai-tool-to-genai.test.ts +296 -0
  192. package/src/ai-tool-to-genai.ts +282 -0
  193. package/src/ai-tool.ts +39 -0
  194. package/src/bin.ts +108 -0
  195. package/src/bot-token.test.ts +171 -0
  196. package/src/bot-token.ts +159 -0
  197. package/src/channel-management.ts +172 -0
  198. package/src/cli-parsing.test.ts +132 -0
  199. package/src/cli.ts +3605 -0
  200. package/src/commands/abort.ts +112 -0
  201. package/src/commands/action-buttons.ts +376 -0
  202. package/src/commands/add-project.ts +152 -0
  203. package/src/commands/agent.ts +404 -0
  204. package/src/commands/ask-question.ts +330 -0
  205. package/src/commands/compact.ts +157 -0
  206. package/src/commands/context-usage.ts +199 -0
  207. package/src/commands/create-new-project.ts +179 -0
  208. package/src/commands/diff.ts +165 -0
  209. package/src/commands/file-upload.ts +389 -0
  210. package/src/commands/fork.ts +320 -0
  211. package/src/commands/gemini-apikey.ts +104 -0
  212. package/src/commands/login.ts +634 -0
  213. package/src/commands/mention-mode.ts +77 -0
  214. package/src/commands/merge-worktree.ts +177 -0
  215. package/src/commands/model.ts +961 -0
  216. package/src/commands/permissions.ts +261 -0
  217. package/src/commands/queue.ts +296 -0
  218. package/src/commands/remove-project.ts +155 -0
  219. package/src/commands/restart-opencode-server.ts +162 -0
  220. package/src/commands/resume.ts +242 -0
  221. package/src/commands/run-command.ts +123 -0
  222. package/src/commands/session-id.ts +109 -0
  223. package/src/commands/session.ts +250 -0
  224. package/src/commands/share.ts +106 -0
  225. package/src/commands/types.ts +25 -0
  226. package/src/commands/undo-redo.ts +221 -0
  227. package/src/commands/unset-model.ts +189 -0
  228. package/src/commands/upgrade.ts +52 -0
  229. package/src/commands/user-command.ts +193 -0
  230. package/src/commands/verbosity.ts +88 -0
  231. package/src/commands/worktree-settings.ts +79 -0
  232. package/src/commands/worktree.ts +431 -0
  233. package/src/condense-memory.ts +36 -0
  234. package/src/config.ts +148 -0
  235. package/src/database.ts +1530 -0
  236. package/src/db.test.ts +60 -0
  237. package/src/db.ts +190 -0
  238. package/src/discord-api.ts +35 -0
  239. package/src/discord-bot.ts +1316 -0
  240. package/src/discord-utils.test.ts +132 -0
  241. package/src/discord-utils.ts +767 -0
  242. package/src/errors.ts +213 -0
  243. package/src/escape-backticks.test.ts +469 -0
  244. package/src/format-tables.test.ts +223 -0
  245. package/src/format-tables.ts +145 -0
  246. package/src/forum-sync/config.ts +92 -0
  247. package/src/forum-sync/discord-operations.ts +241 -0
  248. package/src/forum-sync/index.ts +9 -0
  249. package/src/forum-sync/markdown.ts +176 -0
  250. package/src/forum-sync/sync-to-discord.ts +595 -0
  251. package/src/forum-sync/sync-to-files.ts +294 -0
  252. package/src/forum-sync/types.ts +175 -0
  253. package/src/forum-sync/watchers.ts +454 -0
  254. package/src/genai-worker-wrapper.ts +164 -0
  255. package/src/genai-worker.ts +386 -0
  256. package/src/genai.ts +321 -0
  257. package/src/generated/browser.ts +109 -0
  258. package/src/generated/client.ts +131 -0
  259. package/src/generated/commonInputTypes.ts +512 -0
  260. package/src/generated/enums.ts +46 -0
  261. package/src/generated/internal/class.ts +362 -0
  262. package/src/generated/internal/prismaNamespace.ts +2251 -0
  263. package/src/generated/internal/prismaNamespaceBrowser.ts +308 -0
  264. package/src/generated/models/bot_api_keys.ts +1288 -0
  265. package/src/generated/models/bot_tokens.ts +1577 -0
  266. package/src/generated/models/channel_agents.ts +1256 -0
  267. package/src/generated/models/channel_directories.ts +2104 -0
  268. package/src/generated/models/channel_mention_mode.ts +1300 -0
  269. package/src/generated/models/channel_models.ts +1288 -0
  270. package/src/generated/models/channel_verbosity.ts +1224 -0
  271. package/src/generated/models/channel_worktrees.ts +1308 -0
  272. package/src/generated/models/forum_sync_configs.ts +1452 -0
  273. package/src/generated/models/global_models.ts +1288 -0
  274. package/src/generated/models/ipc_requests.ts +1485 -0
  275. package/src/generated/models/part_messages.ts +1302 -0
  276. package/src/generated/models/scheduled_tasks.ts +2320 -0
  277. package/src/generated/models/session_agents.ts +1086 -0
  278. package/src/generated/models/session_models.ts +1114 -0
  279. package/src/generated/models/session_start_sources.ts +1408 -0
  280. package/src/generated/models/thread_sessions.ts +1599 -0
  281. package/src/generated/models/thread_worktrees.ts +1352 -0
  282. package/src/generated/models.ts +29 -0
  283. package/src/heap-monitor.ts +121 -0
  284. package/src/hrana-server.test.ts +428 -0
  285. package/src/hrana-server.ts +547 -0
  286. package/src/image-utils.ts +149 -0
  287. package/src/interaction-handler.ts +461 -0
  288. package/src/ipc-polling.ts +325 -0
  289. package/src/kimaki-digital-twin.e2e.test.ts +201 -0
  290. package/src/limit-heading-depth.test.ts +116 -0
  291. package/src/limit-heading-depth.ts +26 -0
  292. package/src/logger.ts +203 -0
  293. package/src/markdown.test.ts +360 -0
  294. package/src/markdown.ts +410 -0
  295. package/src/message-formatting.test.ts +81 -0
  296. package/src/message-formatting.ts +549 -0
  297. package/src/openai-realtime.ts +362 -0
  298. package/src/opencode-plugin-loading.e2e.test.ts +112 -0
  299. package/src/opencode-plugin.test.ts +108 -0
  300. package/src/opencode-plugin.ts +652 -0
  301. package/src/opencode.ts +554 -0
  302. package/src/privacy-sanitizer.ts +142 -0
  303. package/src/schema.sql +158 -0
  304. package/src/sentry.ts +137 -0
  305. package/src/session-handler/state.ts +232 -0
  306. package/src/session-handler.ts +2668 -0
  307. package/src/session-search.test.ts +50 -0
  308. package/src/session-search.ts +148 -0
  309. package/src/startup-service.ts +200 -0
  310. package/src/system-message.ts +568 -0
  311. package/src/task-runner.ts +425 -0
  312. package/src/task-schedule.test.ts +84 -0
  313. package/src/task-schedule.ts +287 -0
  314. package/src/thinking-utils.ts +61 -0
  315. package/src/thread-message-queue.e2e.test.ts +997 -0
  316. package/src/tools.ts +432 -0
  317. package/src/unnest-code-blocks.test.ts +679 -0
  318. package/src/unnest-code-blocks.ts +168 -0
  319. package/src/upgrade.ts +127 -0
  320. package/src/utils.ts +145 -0
  321. package/src/voice-handler.ts +852 -0
  322. package/src/voice.test.ts +219 -0
  323. package/src/voice.ts +444 -0
  324. package/src/wait-session.ts +147 -0
  325. package/src/worker-types.ts +64 -0
  326. package/src/worktree-utils.ts +988 -0
  327. package/src/xml.test.ts +38 -0
  328. package/src/xml.ts +121 -0
@@ -0,0 +1,287 @@
1
+ // Scheduled task parsing utilities for `send --send-at` and task runner execution.
2
+
3
+ import { CronExpressionParser } from 'cron-parser'
4
+ import * as errore from 'errore'
5
+
6
+ export type ScheduledTaskPayload =
7
+ | {
8
+ kind: 'thread'
9
+ threadId: string
10
+ prompt: string
11
+ agent: string | null
12
+ model: string | null
13
+ username: string | null
14
+ userId: string | null
15
+ }
16
+ | {
17
+ kind: 'channel'
18
+ channelId: string
19
+ prompt: string
20
+ name: string | null
21
+ notifyOnly: boolean
22
+ worktreeName: string | null
23
+ agent: string | null
24
+ model: string | null
25
+ username: string | null
26
+ userId: string | null
27
+ }
28
+
29
+ export type ParsedSendAt =
30
+ | {
31
+ scheduleKind: 'at'
32
+ runAt: Date
33
+ cronExpr: null
34
+ timezone: null
35
+ nextRunAt: Date
36
+ }
37
+ | {
38
+ scheduleKind: 'cron'
39
+ runAt: null
40
+ cronExpr: string
41
+ timezone: string
42
+ nextRunAt: Date
43
+ }
44
+
45
+ const UTC_SEND_AT_DATE_REGEX =
46
+ /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}(?::\d{2}(?:\.\d{1,3})?)?Z$/
47
+
48
+ export function getLocalTimeZone(): string {
49
+ const tz = Intl.DateTimeFormat().resolvedOptions().timeZone
50
+ if (!tz) {
51
+ return 'UTC'
52
+ }
53
+ return tz
54
+ }
55
+
56
+ export function getPromptPreview(prompt: string): string {
57
+ const normalized = prompt.replace(/\s+/g, ' ').trim()
58
+ if (normalized.length <= 120) {
59
+ return normalized
60
+ }
61
+ return `${normalized.slice(0, 117)}...`
62
+ }
63
+
64
+ function parseUtcSendAtDate({
65
+ value,
66
+ now,
67
+ }: {
68
+ value: string
69
+ now: Date
70
+ }): Date | Error | null {
71
+ const looksLikeDate = value.includes('T') || /^\d{4}-\d{2}-\d{2}/.test(value)
72
+ if (!looksLikeDate) {
73
+ return null
74
+ }
75
+
76
+ if (!UTC_SEND_AT_DATE_REGEX.test(value)) {
77
+ return new Error(
78
+ `--send-at date must be UTC ISO format ending with Z (example: 2026-03-01T09:00:00Z). Received: ${value}`,
79
+ )
80
+ }
81
+
82
+ const runAt = new Date(value)
83
+ if (Number.isNaN(runAt.getTime())) {
84
+ return new Error(`Invalid UTC date for --send-at: ${value}`)
85
+ }
86
+
87
+ if (runAt.getTime() <= now.getTime()) {
88
+ return new Error(`--send-at date must be in the future (UTC): ${value}`)
89
+ }
90
+
91
+ return runAt
92
+ }
93
+
94
+ export function parseSendAtValue({
95
+ value,
96
+ now,
97
+ timezone,
98
+ }: {
99
+ value: string
100
+ now: Date
101
+ timezone: string
102
+ }): ParsedSendAt | Error {
103
+ const trimmed = value.trim()
104
+ if (!trimmed) {
105
+ return new Error('--send-at cannot be empty')
106
+ }
107
+
108
+ const utcDateResult = parseUtcSendAtDate({ value: trimmed, now })
109
+ if (utcDateResult instanceof Error) {
110
+ return utcDateResult
111
+ }
112
+ if (utcDateResult) {
113
+ return {
114
+ scheduleKind: 'at',
115
+ runAt: utcDateResult,
116
+ cronExpr: null,
117
+ timezone: null,
118
+ nextRunAt: utcDateResult,
119
+ }
120
+ }
121
+
122
+ const looksLikeCron =
123
+ trimmed.startsWith('@') || trimmed.split(/\s+/).length >= 5
124
+ if (looksLikeCron) {
125
+ const nextRunAtResult = getNextCronRun({
126
+ cronExpr: trimmed,
127
+ timezone,
128
+ from: now,
129
+ })
130
+ if (!(nextRunAtResult instanceof Error)) {
131
+ return {
132
+ scheduleKind: 'cron',
133
+ runAt: null,
134
+ cronExpr: trimmed,
135
+ timezone,
136
+ nextRunAt: nextRunAtResult,
137
+ }
138
+ }
139
+ }
140
+
141
+ const cronResult = getNextCronRun({ cronExpr: trimmed, timezone, from: now })
142
+ if (cronResult instanceof Error) {
143
+ return new Error(
144
+ `Invalid --send-at value: "${trimmed}". Use UTC ISO date/time ending in Z or a cron expression.`,
145
+ {
146
+ cause: cronResult,
147
+ },
148
+ )
149
+ }
150
+
151
+ return {
152
+ scheduleKind: 'cron',
153
+ runAt: null,
154
+ cronExpr: trimmed,
155
+ timezone,
156
+ nextRunAt: cronResult,
157
+ }
158
+ }
159
+
160
+ export function getNextCronRun({
161
+ cronExpr,
162
+ timezone,
163
+ from,
164
+ }: {
165
+ cronExpr: string
166
+ timezone: string
167
+ from: Date
168
+ }): Date | Error {
169
+ const parsed = errore.try({
170
+ try: () => {
171
+ return CronExpressionParser.parse(cronExpr, {
172
+ currentDate: from,
173
+ tz: timezone,
174
+ })
175
+ },
176
+ catch: (error) => {
177
+ return new Error(`Invalid cron expression: ${cronExpr}`, { cause: error })
178
+ },
179
+ })
180
+ if (parsed instanceof Error) {
181
+ return parsed
182
+ }
183
+
184
+ const next = errore.try({
185
+ try: () => {
186
+ return parsed.next().toDate()
187
+ },
188
+ catch: (error) => {
189
+ return new Error(`Could not compute next run for cron: ${cronExpr}`, {
190
+ cause: error,
191
+ })
192
+ },
193
+ })
194
+ if (next instanceof Error) {
195
+ return next
196
+ }
197
+
198
+ return next
199
+ }
200
+
201
+ export function serializeScheduledTaskPayload(
202
+ payload: ScheduledTaskPayload,
203
+ ): string {
204
+ return JSON.stringify(payload)
205
+ }
206
+
207
+ function isRecord(value: unknown): value is Record<string, unknown> {
208
+ return typeof value === 'object' && value !== null
209
+ }
210
+
211
+ function asString(value: unknown): string | null {
212
+ if (typeof value !== 'string') {
213
+ return null
214
+ }
215
+ return value
216
+ }
217
+
218
+ export function parseScheduledTaskPayload(
219
+ payloadJson: string,
220
+ ): ScheduledTaskPayload | Error {
221
+ const parsed = errore.try({
222
+ try: () => {
223
+ return JSON.parse(payloadJson) as unknown
224
+ },
225
+ catch: (error) => {
226
+ return new Error('Task payload is not valid JSON', { cause: error })
227
+ },
228
+ })
229
+ if (parsed instanceof Error) {
230
+ return parsed
231
+ }
232
+ if (!isRecord(parsed)) {
233
+ return new Error('Task payload must be an object')
234
+ }
235
+
236
+ const kind = asString(parsed.kind)
237
+ if (kind === 'thread') {
238
+ const threadId = asString(parsed.threadId)
239
+ const prompt = asString(parsed.prompt)
240
+ const agent = asString(parsed.agent)
241
+ const model = asString(parsed.model)
242
+ const username = asString(parsed.username)
243
+ const userId = asString(parsed.userId)
244
+ if (!threadId || !prompt) {
245
+ return new Error('Thread task payload requires threadId and prompt')
246
+ }
247
+ return {
248
+ kind: 'thread',
249
+ threadId,
250
+ prompt,
251
+ agent,
252
+ model,
253
+ username,
254
+ userId,
255
+ }
256
+ }
257
+
258
+ if (kind === 'channel') {
259
+ const channelId = asString(parsed.channelId)
260
+ const prompt = asString(parsed.prompt)
261
+ const nameValue = parsed.name
262
+ const name = typeof nameValue === 'string' ? nameValue : null
263
+ const notifyOnly = parsed.notifyOnly === true
264
+ const worktreeName = asString(parsed.worktreeName)
265
+ const agent = asString(parsed.agent)
266
+ const model = asString(parsed.model)
267
+ const username = asString(parsed.username)
268
+ const userId = asString(parsed.userId)
269
+ if (!channelId || !prompt) {
270
+ return new Error('Channel task payload requires channelId and prompt')
271
+ }
272
+ return {
273
+ kind: 'channel',
274
+ channelId,
275
+ prompt,
276
+ name,
277
+ notifyOnly,
278
+ worktreeName,
279
+ agent,
280
+ model,
281
+ username,
282
+ userId,
283
+ }
284
+ }
285
+
286
+ return new Error('Task payload has unknown kind')
287
+ }
@@ -0,0 +1,61 @@
1
+ // Utilities for extracting and matching model variant (thinking level) values
2
+ // from the provider.list() API response. Used by model selector and session handler
3
+ // to validate variant preferences against what the current model actually supports.
4
+
5
+ export type ThinkingProvider = {
6
+ id: string
7
+ models?: Record<string, unknown>
8
+ }
9
+
10
+ function getModelVariants(model: unknown): Record<string, unknown> | undefined {
11
+ if (!model || typeof model !== 'object') {
12
+ return undefined
13
+ }
14
+
15
+ const variants = (model as { variants?: unknown }).variants
16
+ if (!variants || typeof variants !== 'object') {
17
+ return undefined
18
+ }
19
+
20
+ return variants as Record<string, unknown>
21
+ }
22
+
23
+ export function getThinkingValuesForModel({
24
+ providers,
25
+ providerId,
26
+ modelId,
27
+ }: {
28
+ providers: ThinkingProvider[]
29
+ providerId: string
30
+ modelId: string
31
+ }): string[] {
32
+ const provider = providers.find((candidateProvider) => {
33
+ return candidateProvider.id === providerId
34
+ })
35
+ const model = provider?.models?.[modelId]
36
+ const variants = getModelVariants(model)
37
+ if (!variants) {
38
+ return []
39
+ }
40
+
41
+ return Object.keys(variants).filter((variant) => {
42
+ return variant.trim().length > 0
43
+ })
44
+ }
45
+
46
+ export function matchThinkingValue({
47
+ requestedValue,
48
+ availableValues,
49
+ }: {
50
+ requestedValue: string
51
+ availableValues: string[]
52
+ }): string | undefined {
53
+ const normalizedRequestedValue = requestedValue.trim().toLowerCase()
54
+ if (!normalizedRequestedValue) {
55
+ return undefined
56
+ }
57
+
58
+ return availableValues.find((availableValue) => {
59
+ return availableValue.toLowerCase() === normalizedRequestedValue
60
+ })
61
+ }