Jaspers Hub

jaspers / agents

pluginOfficialReviewed★ 0↓ 1

Research Agents: Lets users define research agents that answer chat questions from SEC filings, uploaded documents, and the web, with every figure cited.

2.3 KB
1import type { BackendContext } from '@jaspers-ai/sdk'
2import { openTickers } from './chats.ts'
3import { publishDocs } from './doc-files.ts'
4import { errorText } from './errors.ts'
5import { SERVER } from './research.ts'
6import { companies } from './sec-files.ts'
7import { companyStatuses } from './sec.ts'
8
9// What the view shows of the research store, published while the plugin runs: whether the Terminal
10// is signed in to it (the connection is ready), and each company the open conversations touch, with
11// how far its reading and embedding are. Polled every two seconds while anything is moving, every
12// ten while nothing is.
13
14const BUSY_MS = 2_000
15const IDLE_MS = 10_000
16
17/** What the view gates on: the store is reached as the account. */
18export interface Setup {
19 signedIn: boolean
20 /** The connection's status when it is not ready: needs-sign-in, error, starting. */
21 status: string
22 error: string | null
23}
24
25let watching = false
26/** Whether the account's documents have been listed since the store last answered: once, at the first ready. */
27let docsListed = false
28
29export function watchStatus(ctx: BackendContext): void {
30 if (watching) return
31 watching = true
32 const tick = async (): Promise<void> => {
33 let busy = true
34 try {
35 busy = await publishStatus(ctx)
36 } catch (err) {
37 console.warn(`status: ${errorText(err, 'failed')}`)
38 }
39 setTimeout(() => void tick(), busy ? BUSY_MS : IDLE_MS)
40 }
41 void tick()
42}
43
44/** Publishes the setup and the companies; true while anything is still moving. */
45export async function publishStatus(ctx: BackendContext): Promise<boolean> {
46 const [connection] = await ctx.tools.connections({ connections: [SERVER] })
47 const status = connection?.status ?? 'starting'
48 if (status !== 'ready') {
49 docsListed = false
50 ctx.live.set('setup', { signedIn: false, status, error: null } satisfies Setup)
51 ctx.live.set('sec', [])
52 return status === 'starting'
53 }
54 ctx.live.set('setup', { signedIn: true, status, error: null } satisfies Setup)
55 if (!docsListed) {
56 // The account's documents live in the store: listed as soon as it answers, then after each change.
57 await publishDocs(ctx)
58 docsListed = true
59 }
60 const held = await companies(ctx, openTickers())
61 ctx.live.set('sec', companyStatuses(held))
62 return held.some(
63 (c) => c.unembedded > 0 || (c.job !== null && ['queued', 'listing', 'reading'].includes(c.job.state)),
64 )
65}