| 162 | if (!Array.isArray(input) || input.some((handle) => typeof handle !== 'string')) { |
| 163 | return { ok: false, error: 'to must be an array of agent handles' }; |
| 164 | } |
| 165 | if (input.length === 0) return { ok: false, error: 'to must name at least one agent' }; |
| 166 | return { ok: true, to: input as string[] }; |
| 167 | } |
| 168 | |
| 169 | /** `kickoff: {to, text}` — the first message into a new kild, addressed like any other. */ |
| 170 | function kickoffSpec( |
| 171 | input: unknown, |
| 172 | ): { ok: true; to: string[]; text: string } | { ok: false; error: string } { |
| 173 | if (typeof input !== 'object' || input === null) { |
| 174 | return { ok: false, error: 'kickoff must be an object: {to: [handle], text}' }; |
| 175 | } |
| 176 | const { to, text } = input as { to?: unknown; text?: unknown }; |
| 177 | if (typeof text !== 'string' || !text.trim()) { |
| 178 | return { ok: false, error: 'kickoff.text required' }; |
| 179 | } |
| 180 | const parsed = recipients(to); |
| 181 | if (!parsed.ok) return { ok: false, error: `kickoff.${parsed.error}` }; |
| 182 | return { ok: true, to: parsed.to, text }; |
| 183 | } |
| 184 | |
| 185 | function envRecord(input: unknown): Record<string, string> | undefined { |
| 186 | if (input === undefined) return undefined; |
| 187 | if (typeof input !== 'object' || input === null) return undefined; |
| 188 | const env: Record<string, string> = {}; |
| 189 | for (const [key, value] of Object.entries(input)) { |
| 190 | if (typeof value !== 'string') return undefined; |