(method: string, params?: Record<string, unknown>)
| 215 | |
| 216 | // ── client → server RPCs ───────────────────────────────────────────────── |
| 217 | request<T = unknown>(method: string, params?: Record<string, unknown>): Promise<T> { |
| 218 | const p = (params ?? {}) as Record<string, unknown> |
| 219 | switch (method) { |
| 220 | // ── startup handshake ──────────────────────────────────────────────── |
| 221 | case 'commands.catalog': { |
| 222 | const pairs = SLASHES.map(s => [s.name, s.desc] as [string, string]) |
| 223 | const canon: Record<string, string> = {} |
| 224 | for (const s of SLASHES) canon[s.name] = s.name |
| 225 | return Promise.resolve({ canon, categories: [], pairs, skill_count: 0, sub: {} } as T) |
| 226 | } |
| 227 | case 'complete.slash': { |
| 228 | const text = String(p.text ?? '').toLowerCase() || '/' |
| 229 | const items = SLASHES.filter(s => s.name.toLowerCase().startsWith(text)).map(s => ({ |
| 230 | display: s.name, |
| 231 | meta: s.desc, |
| 232 | text: s.name |
| 233 | })) |
| 234 | return Promise.resolve({ items, replace_from: 1 } as T) |
| 235 | } |
| 236 | case 'complete.path': |
| 237 | // @-file mentions: serve workspace file completions from disk (the hook |
| 238 | // computes the replace offset; we just return matching entries). |
| 239 | return Promise.resolve({ items: this.completePath(String(p.word ?? '')) } as T) |
| 240 | case 'config.get': { |
| 241 | // Settings slashes read config; only 'full' maps to clawcodex settings. |
| 242 | if (String(p.key ?? '') === 'full') { |
| 243 | return this.controlQuery('get_settings', {}).then(s => (s ?? {}) as T) |
| 244 | } |
| 245 | return Promise.resolve({} as T) |
| 246 | } |
| 247 | case 'config.set': { |
| 248 | // Route clawcodex-backed settings to control_requests; display-only prefs |
| 249 | // (mouse/details/statusbar/skin/…) have no backend and apply locally, so |
| 250 | // accept them silently. |
| 251 | const key = String(p.key ?? '') |
| 252 | const value = p.value |
| 253 | if (key === 'model') this.sendControl('set_model', { model: value }) |
| 254 | else if (key === 'permission_mode') this.sendControl('set_permission_mode', { mode: value }) |
| 255 | else if (key === 'effort' || key === 'reasoning') this.sendControl('set_effort', { effort: value }) |
| 256 | else if (key === 'provider') this.sendControl('set_provider', { provider: value }) |
| 257 | else if (key === 'thinking') this.sendControl('set_thinking', { action: value }) |
| 258 | return Promise.resolve({ ok: true } as T) |
| 259 | } |
| 260 | case 'session.activate': |
| 261 | case 'session.create': |
| 262 | case 'session.resume': |
| 263 | // clawcodex runs a single agent-server session; hand back its id once |
| 264 | // system/init has set it. The app then enables the composer. |
| 265 | return this.readyPromise.then(() => ({ info: this.sessionInfo ?? undefined, session_id: this.sessionId }) as T) |
| 266 | case 'setup.status': |
| 267 | return Promise.resolve({ provider_configured: true } as T) |
| 268 | |
| 269 | // ── runtime ────────────────────────────────────────────────────────── |
| 270 | case 'model.options': |
| 271 | return this.controlQuery('get_settings', {}).then((r: any) => { |
| 272 | const models: string[] = Array.isArray(r?.available_models) ? r.available_models : [] |
| 273 | const provider = String(r?.provider ?? 'clawcodex') |
| 274 | return { |
no test coverage detected