@clien-ai/mcp 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.
@@ -0,0 +1,453 @@
1
+ /**
2
+ * `clien_research` — the hero tool.
3
+ *
4
+ * Contract (summary):
5
+ * 1. Ensure a valid bearer token (triggers OAuth on first use).
6
+ * 2. POST /api/validate/start -> { jobId }
7
+ * 3. Poll /api/validate/{jobId}/events every 5s, forward progress to the
8
+ * caller via MCP notifications/progress.
9
+ * 4. On terminal status, GET /api/validate/{jobId}/report (Accept: application/json)
10
+ * -> { reportMarkdown, reportData }.
11
+ * 5. Return { content: [{ type: 'text', text: reportMarkdown }],
12
+ * _meta: { report_data, job_id, status, project_id } }.
13
+ *
14
+ * Error mapping:
15
+ * - 402 -> tool error "Insufficient credits ..."
16
+ * - 401 mid-poll -> session.refresh() once; if still 401, surface
17
+ * "session expired — re-run to re-authenticate"
18
+ * with jobId so the user can recover via email link.
19
+ * - 5xx -> up to 3 backoff retries, then surface with jobId.
20
+ *
21
+ * Timeout behavior:
22
+ * The MCP SDK's progress notifications reset the caller's timeout when
23
+ * `resetTimeoutOnProgress: true` is set on the *caller* side. We emit one
24
+ * notification per events batch, which — per Unit 0's spike — is sufficient
25
+ * to keep Claude Code's 25-min synchronous tool call alive.
26
+ */
27
+ import { z } from 'zod';
28
+ import { OAuthError } from '../auth/oauth.js';
29
+ import { ResearchReportDataSchema } from '../types/report.js';
30
+ // Per-endpoint timeouts (#173). Node fetch has no implicit timeout, so a hung
31
+ // Supabase or Clien.ai endpoint would otherwise wedge the MCP server forever.
32
+ const START_TIMEOUT_MS = 30_000;
33
+ const EVENTS_POLL_TIMEOUT_MS = 30_000;
34
+ const REPORT_FETCH_TIMEOUT_MS = 60_000;
35
+ export const ResearchInputSchema = z.object({
36
+ idea: z
37
+ .string()
38
+ .min(1, 'idea must be non-empty')
39
+ .max(10_000, 'idea too long')
40
+ .describe('The product idea, feature hypothesis, or research question to validate. ' +
41
+ 'Can be a one-liner or several paragraphs — more context yields a better report.'),
42
+ depth: z
43
+ .enum(['quick', 'deep'])
44
+ .optional()
45
+ .default('deep')
46
+ .describe('"deep" (default) runs a 15-25 minute full research pass. ' +
47
+ '"quick" caps cost and runs a tighter pass.'),
48
+ project_id: z
49
+ .string()
50
+ .uuid()
51
+ .optional()
52
+ .describe('Optional Clien.ai project UUID to attach this run to. When omitted, ' +
53
+ 'the run is standalone and not associated with any project.'),
54
+ // Cap aligned with PROJECT_NAME_MAX (100) on the server side. Hardcoded
55
+ // because the @clien-ai/mcp package is published separately and cannot
56
+ // import from the Next.js app. Keep both sides in sync if either changes.
57
+ project_name: z
58
+ .string()
59
+ .min(1, 'project_name must be non-empty')
60
+ .max(100, 'project_name too long (max 100 chars)')
61
+ .optional()
62
+ .describe('Name for a new Clien.ai project to attach this run to. You SHOULD ' +
63
+ 'supply this whenever `project_id` is not set, otherwise the ' +
64
+ 'resulting report will not be discoverable in the Clien.ai web UI. ' +
65
+ 'Derive a short (2-4 word) human-readable name from the idea — ' +
66
+ 'typically the product name. Guidance on when to create a new ' +
67
+ 'project vs. reuse an existing one: create a NEW project (supply a ' +
68
+ 'fresh `project_name`) for each distinct research idea or product ' +
69
+ 'concept; REUSE an existing `project_id` only when the user ' +
70
+ 'explicitly asks to add follow-up research to a prior run on the ' +
71
+ 'same idea. Ignored when `project_id` is also provided.'),
72
+ });
73
+ const defaultSleep = (ms) => new Promise((r) => setTimeout(r, ms));
74
+ const TERMINAL_STATUSES = new Set(['complete', 'failed', 'cancelled']);
75
+ // Roughly map phase -> coarse progress (0..1). We emit progress on every
76
+ // events batch; when the job reports totalCostCents we include it in meta.
77
+ const PHASE_PROGRESS = {
78
+ initialising: 0.05,
79
+ researching: 0.4,
80
+ synthesising: 0.85,
81
+ complete: 1,
82
+ failed: 1,
83
+ cancelled: 1,
84
+ };
85
+ function categorizeFetchError(status) {
86
+ if (status === 401)
87
+ return 'auth';
88
+ if (status === 402)
89
+ return 'credits';
90
+ if (status >= 400 && status < 500)
91
+ return 'client';
92
+ if (status >= 500)
93
+ return 'server';
94
+ return 'other';
95
+ }
96
+ export class ResearchToolError extends Error {
97
+ userMessage;
98
+ jobId;
99
+ constructor(userMessage, opts = {}) {
100
+ super(userMessage);
101
+ this.userMessage = userMessage;
102
+ this.jobId = opts.jobId;
103
+ if (opts.cause !== undefined)
104
+ this.cause = opts.cause;
105
+ }
106
+ }
107
+ /**
108
+ * OAuth error codes we treat as transient (network/availability) rather than
109
+ * auth-fatal. Anything not in this set is classified as `auth` and triggers
110
+ * the user-visible re-auth path. Positive list (allow-list) instead of a
111
+ * single-code negative gate so future codes default to the safer auth path.
112
+ */
113
+ const TRANSIENT_OAUTH_CODES = new Set([
114
+ 'oauth_network_error',
115
+ 'oauth_timeout',
116
+ 'lock_unavailable',
117
+ ]);
118
+ async function apiCall(ctx, method, path, opts = {}) {
119
+ const url = `${ctx.config.serverUrl}${path}`;
120
+ const doFetch = async (token) => {
121
+ const headers = {
122
+ Authorization: `Bearer ${token}`,
123
+ Accept: opts.acceptJson === false ? '*/*' : 'application/json',
124
+ ...(opts.headers ?? {}),
125
+ };
126
+ const body = opts.body === undefined ? undefined : JSON.stringify(opts.body);
127
+ if (body !== undefined && !headers['Content-Type']) {
128
+ headers['Content-Type'] = 'application/json';
129
+ }
130
+ const init = { method, headers, body };
131
+ if (opts.timeoutMs !== undefined) {
132
+ init.signal = AbortSignal.timeout(opts.timeoutMs);
133
+ }
134
+ return ctx.fetch(url, init);
135
+ };
136
+ let response = (await doFetch(ctx.session.accessToken));
137
+ if (response.status === 401 && opts.retryOn401 !== false) {
138
+ try {
139
+ const fresh = await ctx.session.refresh();
140
+ response = (await doFetch(fresh));
141
+ }
142
+ catch (err) {
143
+ // Distinguish: transient (network/lock/timeout) vs auth (revoked).
144
+ // Positive allow-list means future OAuth codes default to `auth` —
145
+ // safer to surface re-auth than to mistakenly retry on a real
146
+ // credential failure.
147
+ const isTransient = err instanceof OAuthError && TRANSIENT_OAUTH_CODES.has(err.code);
148
+ response.refreshError = {
149
+ kind: isTransient ? 'transient' : 'auth',
150
+ message: err instanceof Error ? err.message : String(err),
151
+ };
152
+ }
153
+ }
154
+ return response;
155
+ }
156
+ // ------------------------------------------------------------------
157
+ // Main tool handler
158
+ // ------------------------------------------------------------------
159
+ export async function runResearch(input, deps) {
160
+ const parsed = ResearchInputSchema.safeParse(input);
161
+ if (!parsed.success) {
162
+ const details = parsed.error.issues
163
+ .map((i) => `${i.path.join('.') || 'input'}: ${i.message}`)
164
+ .join('; ');
165
+ throw new ResearchToolError(`Invalid input — ${details}`);
166
+ }
167
+ const { idea, depth, project_id, project_name } = parsed.data;
168
+ const fetchImpl = deps.fetch ?? fetch;
169
+ const sleepImpl = deps.sleep ?? defaultSleep;
170
+ const pollIntervalMs = deps.pollIntervalMs ?? deps.config.pollIntervalMs;
171
+ const ctx = {
172
+ config: deps.config,
173
+ session: deps.session,
174
+ fetch: fetchImpl,
175
+ };
176
+ // 1. Start. Forward `depth` so the backend can apply the matching
177
+ // budget/model defaults — see resolveValidationConfig in the Next.js
178
+ // app's app/lib/validation/schemas.ts.
179
+ const startBody = {
180
+ message: idea,
181
+ depth,
182
+ };
183
+ if (project_id)
184
+ startBody.projectId = project_id;
185
+ if (project_name)
186
+ startBody.projectName = project_name;
187
+ let startResponse;
188
+ try {
189
+ startResponse = await apiCall(ctx, 'POST', '/api/validate/start', {
190
+ body: startBody,
191
+ timeoutMs: START_TIMEOUT_MS,
192
+ });
193
+ }
194
+ catch (err) {
195
+ const e = err;
196
+ if (e.name === 'AbortError' || e.name === 'TimeoutError') {
197
+ throw new ResearchToolError(`Clien.ai start endpoint timed out after ${START_TIMEOUT_MS / 1000}s. Try again in a moment.`);
198
+ }
199
+ throw err;
200
+ }
201
+ if (!startResponse.ok) {
202
+ // If the 401 came from a refresh failure, the cached session is suspect —
203
+ // invalidate so the next tool invocation rebuilds it from disk.
204
+ if (startResponse.status === 401) {
205
+ deps.invalidateSession?.();
206
+ }
207
+ await handleStartError(startResponse);
208
+ }
209
+ const startJson = (await startResponse.json().catch(() => null));
210
+ if (!startJson?.jobId) {
211
+ throw new ResearchToolError('Clien.ai accepted the request but did not return a job id. Please try again.');
212
+ }
213
+ const jobId = startJson.jobId;
214
+ // projectId may be absent on older deployments; treat missing as null so
215
+ // _meta.project_id is always present in the result shape.
216
+ const startProjectId = startJson.projectId ?? null;
217
+ try {
218
+ await deps.onProgress?.({
219
+ progress: 0,
220
+ total: 1,
221
+ message: `Research started (job ${jobId})`,
222
+ });
223
+ }
224
+ catch {
225
+ // progress is advisory; never let it tank the run
226
+ }
227
+ // 2. Poll events
228
+ let lastSequence = 0;
229
+ let status = startJson.status ?? 'initialising';
230
+ let consecutiveServerErrors = 0;
231
+ let totalCostCents;
232
+ const deadline = deps.maxDurationMs ? Date.now() + deps.maxDurationMs : Number.POSITIVE_INFINITY;
233
+ while (!TERMINAL_STATUSES.has(status)) {
234
+ if (Date.now() > deadline) {
235
+ throw new ResearchToolError(`Research did not complete within the allotted time. Check the web report: ${webJobUrl(deps.config, jobId)}`, { jobId });
236
+ }
237
+ await sleepImpl(pollIntervalMs);
238
+ let eventsResponse;
239
+ try {
240
+ eventsResponse = await apiCall(ctx, 'GET', `/api/validate/${jobId}/events?since=${lastSequence}&limit=100`, { timeoutMs: EVENTS_POLL_TIMEOUT_MS });
241
+ }
242
+ catch (err) {
243
+ const e = err;
244
+ if (e.name === 'AbortError' || e.name === 'TimeoutError') {
245
+ // Treat poll timeouts the same as transient 5xx — backoff and retry.
246
+ consecutiveServerErrors += 1;
247
+ if (consecutiveServerErrors >= 3) {
248
+ throw new ResearchToolError(`Repeated network timeouts polling /events. Your job is still running — check the web report: ${webJobUrl(deps.config, jobId)}`, { jobId });
249
+ }
250
+ const backoffMs = [2000, 5000, 10_000][consecutiveServerErrors - 1] ?? 10_000;
251
+ await sleepImpl(backoffMs);
252
+ continue;
253
+ }
254
+ throw err;
255
+ }
256
+ if (eventsResponse.ok) {
257
+ consecutiveServerErrors = 0;
258
+ const data = (await eventsResponse.json().catch(() => null));
259
+ if (data?.job?.status)
260
+ status = data.job.status;
261
+ if (typeof data?.job?.totalCostCents === 'number') {
262
+ totalCostCents = data.job.totalCostCents;
263
+ }
264
+ if (typeof data?.lastSequence === 'number') {
265
+ lastSequence = data.lastSequence;
266
+ }
267
+ const latestMessage = summarizeEvents(data?.events);
268
+ const phaseProgress = PHASE_PROGRESS[status] ?? 0.5;
269
+ try {
270
+ await deps.onProgress?.({
271
+ progress: phaseProgress,
272
+ total: 1,
273
+ message: latestMessage ?? `phase: ${status}`,
274
+ });
275
+ }
276
+ catch {
277
+ // ignore
278
+ }
279
+ continue;
280
+ }
281
+ const category = categorizeFetchError(eventsResponse.status);
282
+ if (category === 'auth') {
283
+ // Distinguish "transient refresh failure" from "genuinely revoked".
284
+ // The former came from network issues during the refresh attempt and
285
+ // should backoff like any 5xx; the latter requires re-authentication.
286
+ if (eventsResponse.refreshError?.kind === 'transient') {
287
+ consecutiveServerErrors += 1;
288
+ if (consecutiveServerErrors >= 3) {
289
+ throw new ResearchToolError(`Repeated network errors refreshing session. Your job is still running — check the web report: ${webJobUrl(deps.config, jobId)}`, { jobId });
290
+ }
291
+ const backoffMs = [2000, 5000, 10_000][consecutiveServerErrors - 1] ?? 10_000;
292
+ await sleepImpl(backoffMs);
293
+ continue;
294
+ }
295
+ // Genuine auth failure — drop the in-memory cached session so the next
296
+ // tool call rebuilds from disk (or fails fast with `oauth_no_tty`).
297
+ deps.invalidateSession?.();
298
+ throw new ResearchToolError(`Session expired during research. Your run is still in progress — view the report at ${webJobUrl(deps.config, jobId)} or re-run clien_research to re-authenticate.`, { jobId });
299
+ }
300
+ if (category === 'credits') {
301
+ // extremely unlikely mid-run; the server would have 402'd at start.
302
+ throw new ResearchToolError('Insufficient credits — buy more at https://clien.ai/app/billing', { jobId });
303
+ }
304
+ if (category === 'server') {
305
+ consecutiveServerErrors += 1;
306
+ if (consecutiveServerErrors >= 3) {
307
+ throw new ResearchToolError(`Clien.ai is returning server errors. Your job is still running — check the web report: ${webJobUrl(deps.config, jobId)}`, { jobId });
308
+ }
309
+ // Exponential backoff: 2s, 5s, 10s.
310
+ const backoffMs = [2000, 5000, 10_000][consecutiveServerErrors - 1] ?? 10_000;
311
+ await sleepImpl(backoffMs);
312
+ continue;
313
+ }
314
+ // 404 / 400 on events after a successful start is highly unusual — surface.
315
+ const text = await eventsResponse.text().catch(() => '');
316
+ throw new ResearchToolError(`Unexpected error polling events (HTTP ${eventsResponse.status}): ${truncate(text, 200)}`, { jobId });
317
+ }
318
+ // 3. Fetch final report (JSON variant)
319
+ let reportResponse;
320
+ try {
321
+ reportResponse = await apiCall(ctx, 'GET', `/api/validate/${jobId}/report`, { acceptJson: true, timeoutMs: REPORT_FETCH_TIMEOUT_MS });
322
+ }
323
+ catch (err) {
324
+ const e = err;
325
+ if (e.name === 'AbortError' || e.name === 'TimeoutError') {
326
+ throw new ResearchToolError(`Clien.ai report fetch timed out after ${REPORT_FETCH_TIMEOUT_MS / 1000}s. Job ${jobId} completed; view at ${webJobUrl(deps.config, jobId)}.`, { jobId });
327
+ }
328
+ throw err;
329
+ }
330
+ if (!reportResponse.ok) {
331
+ // If the run failed but the report endpoint has no markdown yet, provide
332
+ // the best message we can given we know the job status.
333
+ if (reportResponse.status === 404) {
334
+ throw new ResearchToolError(`The research run ${status === 'failed' ? 'failed' : 'did not produce a report'}. See ${webJobUrl(deps.config, jobId)} for details.`, { jobId });
335
+ }
336
+ throw new ResearchToolError(`Failed to fetch report (HTTP ${reportResponse.status}). Job ${jobId} completed with status "${status}". See ${webJobUrl(deps.config, jobId)}.`, { jobId });
337
+ }
338
+ const reportJson = (await reportResponse.json().catch(() => null));
339
+ if (!reportJson?.reportMarkdown) {
340
+ throw new ResearchToolError(`Report is empty for job ${jobId}. View at ${webJobUrl(deps.config, jobId)}.`, { jobId });
341
+ }
342
+ // Validate report_data against our typed schema. On failure, pass through
343
+ // the raw payload unchanged — never drop data, even if the backend ships a
344
+ // shape we don't yet recognize. The schema is `.passthrough()` so unknown
345
+ // fields survive validation silently.
346
+ const rawReportData = reportJson.reportData ?? null;
347
+ let reportData = rawReportData;
348
+ if (rawReportData) {
349
+ const parsedReport = ResearchReportDataSchema.safeParse(rawReportData);
350
+ if (parsedReport.success) {
351
+ reportData = parsedReport.data;
352
+ }
353
+ else {
354
+ // Stderr only — stdout is the MCP transport. Log the first issue so
355
+ // operators can spot a backend/MCP version drift.
356
+ console.error('[clien-mcp] report_data failed schema validation; passing through raw payload:', parsedReport.error.issues[0]);
357
+ }
358
+ }
359
+ return {
360
+ content: [{ type: 'text', text: reportJson.reportMarkdown }],
361
+ _meta: {
362
+ job_id: jobId,
363
+ status: reportJson.status ?? status,
364
+ project_id: startProjectId,
365
+ report_data: reportData,
366
+ ...(totalCostCents !== undefined ? { total_cost_cents: totalCostCents } : {}),
367
+ },
368
+ };
369
+ }
370
+ async function handleStartError(response) {
371
+ const text = await response.text().catch(() => '');
372
+ let parsed = null;
373
+ try {
374
+ parsed = JSON.parse(text);
375
+ }
376
+ catch {
377
+ // non-JSON body
378
+ }
379
+ const status = response.status;
380
+ if (status === 402 || parsed?.code === 'INSUFFICIENT_CREDITS') {
381
+ // Forward the structured 402 fields set by /api/validate/start so the
382
+ // calling agent can give the user a concrete "you have X, need Y" prompt
383
+ // and surface the env-correct billing URL (production vs preview vs
384
+ // staging). Falls back to a generic message if the server-side body
385
+ // shape ever drifts.
386
+ const balance = typeof parsed?.balance === 'number' ? parsed.balance : null;
387
+ const required = typeof parsed?.required === 'number' ? parsed.required : null;
388
+ const buyUrl = typeof parsed?.buy_url === 'string' && parsed.buy_url.length > 0
389
+ ? parsed.buy_url
390
+ : 'https://clien.ai/app/billing';
391
+ let message;
392
+ if (balance !== null && required !== null) {
393
+ const needed = Math.max(required - balance, 1);
394
+ message =
395
+ `Insufficient credits to start a research run — you have ${balance}, ` +
396
+ `this run needs ${required} (${needed} more). Buy credits at ${buyUrl}.`;
397
+ }
398
+ else {
399
+ message = `Insufficient credits to start a research run. Buy more at ${buyUrl}.`;
400
+ }
401
+ throw new ResearchToolError(message);
402
+ }
403
+ if (status === 403 && parsed?.code === 'BILLING_LIMIT_EXCEEDED') {
404
+ throw new ResearchToolError(parsed?.message ??
405
+ 'Monthly validation limit reached. Upgrade at https://clien.ai/app/billing.');
406
+ }
407
+ if (status === 401) {
408
+ throw new ResearchToolError('Authentication failed. Run clien_research again to re-authenticate.');
409
+ }
410
+ if (status === 409) {
411
+ throw new ResearchToolError(parsed?.message ?? 'A research run is already in progress for this project.');
412
+ }
413
+ if (status === 400) {
414
+ throw new ResearchToolError(parsed?.message ?? `Bad request starting research: ${truncate(text, 300)}`);
415
+ }
416
+ // 503 CIRCUIT_BREAKER_OPEN — Unit 10's third-party-fault breaker. The server
417
+ // body carries the user-facing message ("...try again in 30 minutes"); we
418
+ // forward it instead of collapsing to the generic 5xx "few minutes" wording
419
+ // because the breaker's whole point is signalling a *longer* outage.
420
+ if (status === 503 && parsed?.code === 'CIRCUIT_BREAKER_OPEN') {
421
+ throw new ResearchToolError(parsed?.error ??
422
+ parsed?.message ??
423
+ 'Our research infrastructure is experiencing issues. Please try again in 30 minutes.');
424
+ }
425
+ if (status >= 500) {
426
+ throw new ResearchToolError(`Clien.ai server error (${status}). Please try again in a few minutes.`);
427
+ }
428
+ throw new ResearchToolError(`Failed to start research (HTTP ${status}): ${truncate(text, 200)}`);
429
+ }
430
+ function webJobUrl(config, jobId) {
431
+ return `${config.serverUrl}/app?job=${encodeURIComponent(jobId)}`;
432
+ }
433
+ function summarizeEvents(events) {
434
+ if (!events || events.length === 0)
435
+ return null;
436
+ const last = events[events.length - 1];
437
+ if (!last)
438
+ return null;
439
+ const type = last.type ?? 'event';
440
+ const data = last.data ?? {};
441
+ // Best-effort human summary, kept <100 chars.
442
+ if (typeof data.message === 'string' && data.message.length > 0) {
443
+ return truncate(`${type}: ${data.message}`, 120);
444
+ }
445
+ if (typeof data.phase === 'string') {
446
+ return truncate(`${type}: ${data.phase}`, 120);
447
+ }
448
+ return type;
449
+ }
450
+ function truncate(s, n) {
451
+ return s.length <= n ? s : `${s.slice(0, n - 1)}…`;
452
+ }
453
+ //# sourceMappingURL=research.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"research.js","sourceRoot":"","sources":["../../src/tools/research.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,60 @@
1
+ /**
2
+ * Typed shapes for the structured `report_data` field returned by
3
+ * `clien_research`. These exist so downstream agents can extract competitor
4
+ * info, hypotheses, etc. without hand-rolling JSON parsing.
5
+ *
6
+ * The agent (agent/src/types.ts:ValidationReportData) is the source of truth
7
+ * — this schema mirrors the boundary fields the MCP exposes. Unknown fields
8
+ * pass through (`.passthrough()`) so a backend that adds a new field doesn't
9
+ * break old MCP clients.
10
+ *
11
+ * On parse failure, callers should preserve the raw payload (don't drop data)
12
+ * — see how research.ts handles it.
13
+ */
14
+ import { z } from 'zod';
15
+ /**
16
+ * A competitor discovered during the research run.
17
+ *
18
+ * Per the project's MEMORY note: competitor discovery is the standout value of
19
+ * the report. Making this a typed surface lets downstream agents (e.g. another
20
+ * Claude Code session asked to "look into our top three competitors") extract
21
+ * the list directly from `_meta.report_data.competitors` instead of grepping
22
+ * the markdown.
23
+ */
24
+ // `name` is `z.string()` (NOT min(1)) to mirror the agent's lenient
25
+ // CompetitorSummarySchema. If the agent emits a competitor with an empty
26
+ // name, validation should not fail on the consumer side — let it through and
27
+ // trust the agent's own validation. Stricter local validation here would
28
+ // trigger our passthrough fallback and add stderr noise without preventing
29
+ // the agent's emission.
30
+ //
31
+ // `url` validates URL format when present (matches agent's `.url()`). Still
32
+ // optional because the agent occasionally emits competitors without a URL
33
+ // (e.g. private/internal tools surfaced from interviews).
34
+ export const CompetitorSchema = z
35
+ .object({
36
+ name: z.string(),
37
+ url: z.string().url().optional(),
38
+ positioning: z.string().optional(),
39
+ threat_level: z.enum(['low', 'medium', 'high']).optional(),
40
+ notes: z.string().optional(),
41
+ })
42
+ .passthrough();
43
+ /**
44
+ * The MCP-exposed shape of `_meta.report_data`. All fields optional —
45
+ * different runs surface different sections, and old/new agent versions may
46
+ * differ. `.passthrough()` preserves any backend-added fields the schema
47
+ * doesn't yet name, so this contract evolves additively.
48
+ */
49
+ export const ResearchReportDataSchema = z
50
+ .object({
51
+ competitors: z.array(CompetitorSchema).optional(),
52
+ hypotheses: z.array(z.unknown()).optional(),
53
+ insights: z.array(z.unknown()).optional(),
54
+ keyFindings: z.array(z.string()).optional(),
55
+ recommendations: z.array(z.unknown()).optional(),
56
+ executiveSummary: z.string().optional(),
57
+ overallConfidence: z.number().min(0).max(1).optional(),
58
+ })
59
+ .passthrough();
60
+ //# sourceMappingURL=report.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"report.js","sourceRoot":"","sources":["../../src/types/report.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAEvB;;;;;;;;GAQG;AACH,oEAAoE;AACpE,yEAAyE;AACzE,6EAA6E;AAC7E,yEAAyE;AACzE,2EAA2E;AAC3E,wBAAwB;AACxB,EAAE;AACF,4EAA4E;AAC5E,0EAA0E;AAC1E,0DAA0D;AAC1D,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC;KAC9B,MAAM,CAAC;IACN,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE;IAChB,GAAG,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE;IAChC,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAClC,YAAY,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAC,CAAC,QAAQ,EAAE;IAC1D,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;CAC7B,CAAC;KACD,WAAW,EAAE,CAAA;AAIhB;;;;;GAKG;AACH,MAAM,CAAC,MAAM,wBAAwB,GAAG,CAAC;KACtC,MAAM,CAAC;IACN,WAAW,EAAE,CAAC,CAAC,KAAK,CAAC,gBAAgB,CAAC,CAAC,QAAQ,EAAE;IACjD,UAAU,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,QAAQ,EAAE;IAC3C,QAAQ,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,QAAQ,EAAE;IACzC,WAAW,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,QAAQ,EAAE;IAC3C,eAAe,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,QAAQ,EAAE;IAChD,gBAAgB,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IACvC,iBAAiB,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE;CACvD,CAAC;KACD,WAAW,EAAE,CAAA"}
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Crash-safe file writes via tmp file + rename.
3
+ *
4
+ * Both write paths in this package need the same guarantee: a SIGKILL or
5
+ * power loss between open and flush must NOT leave a half-written file at the
6
+ * target. We get that by writing to `${path}.tmp-${pid}-${Date.now()}` first,
7
+ * then `renameSync` to the final path — `rename(2)` is atomic within a single
8
+ * filesystem on POSIX, so observers see either the old contents or the new
9
+ * contents, never a torn write.
10
+ *
11
+ * Used by:
12
+ * - commands/install.ts → writes ~/.claude.json
13
+ * - auth/storage.ts → writes ~/.clien-ai/tokens.json.enc
14
+ */
15
+ import { existsSync, mkdirSync, renameSync, unlinkSync, writeFileSync, } from 'node:fs';
16
+ import { dirname } from 'node:path';
17
+ /**
18
+ * Write `data` to `path` atomically. Creates the parent directory if missing.
19
+ * On error, best-effort unlink of the tmp file (which may or may not exist).
20
+ */
21
+ export function writeFileAtomic(path, data, options = {}) {
22
+ const mode = options.mode ?? 0o600;
23
+ const dirMode = options.dirMode ?? 0o700;
24
+ const dir = dirname(path);
25
+ if (!existsSync(dir)) {
26
+ mkdirSync(dir, { recursive: true, mode: dirMode });
27
+ }
28
+ const tmp = `${path}.tmp-${process.pid}-${Date.now()}`;
29
+ try {
30
+ writeFileSync(tmp, data, { mode });
31
+ renameSync(tmp, path);
32
+ }
33
+ catch (e) {
34
+ try {
35
+ unlinkSync(tmp);
36
+ }
37
+ catch {
38
+ // tmp may not have been created (e.g. mkdir failed) — benign
39
+ }
40
+ throw e;
41
+ }
42
+ }
43
+ //# sourceMappingURL=atomic-write.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"atomic-write.js","sourceRoot":"","sources":["../../src/util/atomic-write.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EACL,UAAU,EACV,SAAS,EACT,UAAU,EACV,UAAU,EACV,aAAa,GACd,MAAM,SAAS,CAAA;AAChB,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AASnC;;;GAGG;AACH,MAAM,UAAU,eAAe,CAC7B,IAAY,EACZ,IAAqB,EACrB,UAA8B,EAAE;IAEhC,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,IAAI,KAAK,CAAA;IAClC,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,IAAI,KAAK,CAAA;IAExC,MAAM,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IACzB,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;QACrB,SAAS,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,CAAA;IACpD,CAAC;IAED,MAAM,GAAG,GAAG,GAAG,IAAI,QAAQ,OAAO,CAAC,GAAG,IAAI,IAAI,CAAC,GAAG,EAAE,EAAE,CAAA;IACtD,IAAI,CAAC;QACH,aAAa,CAAC,GAAG,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE,CAAC,CAAA;QAClC,UAAU,CAAC,GAAG,EAAE,IAAI,CAAC,CAAA;IACvB,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,IAAI,CAAC;YACH,UAAU,CAAC,GAAG,CAAC,CAAA;QACjB,CAAC;QAAC,MAAM,CAAC;YACP,6DAA6D;QAC/D,CAAC;QACD,MAAM,CAAC,CAAA;IACT,CAAC;AACH,CAAC"}