Jaspers Hub

jaspers / agents

pluginOfficialReviewed★ 0↓ 0

Research Agents: Lets you define research agents that chat and answer with citations from SEC filings, your documents, and the web, via Jaspers' research-store service.

2.5 KB
1// What an answer shows while it is being written: the step it is on, worded from the call it makes,
2// and the model's latest note to itself, its words beside its tool calls or, where the provider hands
3// them back, its thinking. Pure, so the wording is tested.
4
5/** How much of a note the line shows: its end, where the model is now. */
6const THOUGHT_MAX = 280
7
8export interface Working {
9 step: string
10 thought: string | null
11 /** The company add_company is reading in, while it waits. */
12 company?: string
13}
14
15/** A tool call as the working line says it. */
16export function stepOf(call: { name: string; input: Record<string, unknown> }): string {
17 const text = (key: string): string => (typeof call.input[key] === 'string' ? (call.input[key] as string).trim() : '')
18 switch (call.name) {
19 case 'search_prism': {
20 const where = [text('ticker'), text('form'), call.input['fiscal_year'], text('fiscal_period'), text('section')]
21 .filter((part) => part !== '' && part !== undefined && part !== null)
22 .join(' ')
23 return `Searching “${text('query')}”${where ? ` · ${where}` : ''}`
24 }
25 case 'fetch_page': {
26 let host = text('url')
27 try {
28 host = new URL(host).host
29 } catch {
30 // Said as written.
31 }
32 return `Reading ${host}`
33 }
34 case 'search_web':
35 return `Searching the web for “${text('query')}”`
36 case 'calculate':
37 return 'Calculating'
38 case 'add_company': {
39 const [from, to] = [call.input['from'], call.input['to']]
40 return `Reading ${text('ticker')} filings, ${from === to ? from : `${from}–${to}`}`
41 }
42 case 'activate_skill':
43 return `Loading skill ${text('name')}`
44 case 'read_skill_file':
45 return `Reading ${text('skill')}/${text('path')}`
46 default:
47 return `Calling ${call.name}`
48 }
49}
50
51/** The model's note on a round that calls tools: its words, else its thinking where the payload has it. */
52export function thoughtOf(text: string, raw: unknown[] | null): string | null {
53 const said = text.trim() || thinkingIn(raw)
54 if (!said) return null
55 const flat = said.replace(/\s+/g, ' ')
56 return flat.length > THOUGHT_MAX ? `…${flat.slice(-THOUGHT_MAX)}` : flat
57}
58
59/** Anthropic's thinking blocks, the only payload a plugin is handed that carries thinking as words. */
60function thinkingIn(raw: unknown[] | null): string {
61 return (raw ?? [])
62 .map((block) =>
63 block && typeof block === 'object' && (block as { type?: unknown }).type === 'thinking'
64 ? String((block as { thinking?: unknown }).thinking ?? '')
65 : '',
66 )
67 .filter(Boolean)
68 .join(' ')
69 .trim()
70}