@tianmucreations/jeeves 0.3.1 → 0.3.2

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.
Files changed (43) hide show
  1. package/dist/agent/errors.js +1 -1
  2. package/dist/agent/loop.js +34 -1
  3. package/dist/agent/systemPrompt.js +19 -5
  4. package/dist/app.js +23 -1
  5. package/dist/checkpoints/index.js +34 -4
  6. package/dist/commands/clear.js +2 -0
  7. package/dist/commands/help.js +16 -14
  8. package/dist/commands/keys.js +2 -1
  9. package/dist/components/AddressPrompt.js +21 -6
  10. package/dist/components/Footer.js +6 -1
  11. package/dist/components/Header.js +5 -1
  12. package/dist/components/HelpView.js +1 -1
  13. package/dist/components/Input.js +135 -13
  14. package/dist/components/KeysManager.js +33 -18
  15. package/dist/components/ModelPicker.js +27 -8
  16. package/dist/components/OpenRouterConnect.js +114 -0
  17. package/dist/components/ProjectPicker.js +55 -16
  18. package/dist/components/Transcript.js +39 -2
  19. package/dist/components/input-layout.js +129 -26
  20. package/dist/components/markdown.js +185 -0
  21. package/dist/components/transcript-layout.js +50 -2
  22. package/dist/index.js +2 -1
  23. package/dist/ink/mouse.js +39 -6
  24. package/dist/ink/quit.js +21 -0
  25. package/dist/ink/selection.js +128 -0
  26. package/dist/keys/store.js +72 -2
  27. package/dist/platform/address.js +16 -0
  28. package/dist/platform/chat-folder.js +24 -0
  29. package/dist/platform/config.js +3 -0
  30. package/dist/platform/wording.js +10 -0
  31. package/dist/providers/direct.js +8 -2
  32. package/dist/providers/index.js +21 -3
  33. package/dist/providers/ollama.js +8 -2
  34. package/dist/providers/openrouter-signin.js +102 -0
  35. package/dist/providers/openrouter.js +17 -2
  36. package/dist/providers/silence.js +39 -0
  37. package/dist/providers/zai.js +17 -13
  38. package/dist/state/session.js +41 -2
  39. package/dist/tools/index.js +22 -5
  40. package/dist/tools/runBash.js +17 -5
  41. package/dist/tools/web/research.js +119 -21
  42. package/dist/tools/web/zai-search.js +79 -0
  43. package/package.json +2 -1
@@ -0,0 +1,79 @@
1
+ import { ZAI_CODING_BASE_URL } from '../../providers/zai.js';
2
+ // Web search on the GLM Coding Plan: Z.ai's Web Search MCP server, included in every
3
+ // plan (docs.z.ai/devpack/mcp/search-mcp-server). Spoken to directly over MCP's HTTP
4
+ // transport: initialize, then call the one tool it lists, "web_search_prime".
5
+ // Measured 18 Sept 2026 with a real plan key: results come back as a JSON string of
6
+ // [{title, link, content, refer}], where link is often only the site's front page and
7
+ // content a ~150-character summary - weaker than OpenRouter's search, so Jeeves is told
8
+ // to say plainly when a fact can't be confirmed from them.
9
+ export const ZAI_SEARCH_URL = 'https://api.z.ai/api/mcp/web_search_prime/mcp';
10
+ async function rpc(key, body, session, signal) {
11
+ const response = await fetch(ZAI_SEARCH_URL, {
12
+ method: 'POST',
13
+ headers: {
14
+ Authorization: `Bearer ${key}`,
15
+ 'Content-Type': 'application/json',
16
+ Accept: 'application/json, text/event-stream',
17
+ ...(session ? { 'mcp-session-id': session } : {}),
18
+ },
19
+ body: JSON.stringify(body),
20
+ signal,
21
+ });
22
+ const text = await response.text();
23
+ if (!response.ok)
24
+ throw new Error(`Z.ai web search returned ${response.status}: ${text.slice(0, 200)}`);
25
+ // Replies arrive as server-sent events ("data: {...}") or as plain JSON.
26
+ const line = text.split('\n').find((entry) => entry.startsWith('data:'));
27
+ const parsed = line ? JSON.parse(line.slice(5)) : text ? JSON.parse(text) : {};
28
+ return { session: response.headers.get('mcp-session-id') ?? session, body: parsed };
29
+ }
30
+ // The tool's text is a JSON string of the results (sometimes JSON-encoded twice).
31
+ export function parseZaiResults(text) {
32
+ let value = text;
33
+ for (let i = 0; i < 2 && typeof value === 'string'; i++) {
34
+ try {
35
+ value = JSON.parse(value);
36
+ }
37
+ catch {
38
+ break;
39
+ }
40
+ }
41
+ if (!Array.isArray(value))
42
+ return [];
43
+ return value
44
+ .map((item) => item)
45
+ .filter((item) => typeof item.link === 'string' && item.link)
46
+ .map((item) => ({ url: String(item.link), title: String(item.title ?? ''), content: String(item.content ?? '') }));
47
+ }
48
+ export async function zaiSearch(key, query, site, timeoutMs = 45_000) {
49
+ const signal = AbortSignal.timeout(timeoutMs);
50
+ const init = await rpc(key, { jsonrpc: '2.0', id: 1, method: 'initialize', params: { protocolVersion: '2025-06-18', capabilities: {}, clientInfo: { name: 'jeeves', version: '1' } } }, undefined, signal);
51
+ // The server expects to be told initialisation is finished before tool calls.
52
+ await rpc(key, { jsonrpc: '2.0', method: 'notifications/initialized' }, init.session, signal).catch(() => undefined);
53
+ const call = await rpc(key, {
54
+ jsonrpc: '2.0',
55
+ id: 2,
56
+ method: 'tools/call',
57
+ params: { name: 'web_search_prime', arguments: { search_query: query.slice(0, 70), location: 'us', ...(site ? { search_domain_filter: site } : {}) } },
58
+ }, init.session, signal);
59
+ if (call.body.error || call.body.result?.isError) {
60
+ throw new Error(`Z.ai web search: ${call.body.error?.message ?? call.body.result?.content?.[0]?.text ?? 'failed'}`);
61
+ }
62
+ return parseZaiResults(call.body.result?.content?.find((part) => part.type === 'text')?.text ?? '');
63
+ }
64
+ // One reading request on the plan: GLM-5.3-Flash on the coding endpoint, thinking off
65
+ // (reading only quotes the page). Returns the reply text.
66
+ export const ZAI_READING_MODEL = 'glm-5.3-flash';
67
+ export async function zaiRead(key, messages, timeoutMs = 60_000) {
68
+ const response = await fetch(`${ZAI_CODING_BASE_URL}/chat/completions`, {
69
+ method: 'POST',
70
+ headers: { Authorization: `Bearer ${key}`, 'Content-Type': 'application/json' },
71
+ body: JSON.stringify({ model: ZAI_READING_MODEL, messages, max_tokens: 1500, thinking: { type: 'disabled' } }),
72
+ signal: AbortSignal.timeout(timeoutMs),
73
+ });
74
+ const text = await response.text();
75
+ if (!response.ok)
76
+ throw new Error(`Z.ai returned ${response.status}: ${text.slice(0, 200)}`);
77
+ const body = JSON.parse(text);
78
+ return (body.choices?.[0]?.message?.content ?? '').trim();
79
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tianmucreations/jeeves",
3
- "version": "0.3.1",
3
+ "version": "0.3.2",
4
4
  "description": "Your personal assistant in the terminal: say what you need in plain English and Jeeves does the work carefully - asks first, can undo, watches your spending. Auto mode picks the right AI model for you.",
5
5
  "keywords": [
6
6
  "ai",
@@ -71,6 +71,7 @@
71
71
  "ink": "^7.1.1",
72
72
  "ink-spinner": "^5.0.0",
73
73
  "keytar": "^7.9.0",
74
+ "marked": "^18.0.13",
74
75
  "node-notifier": "^10.0.1",
75
76
  "react": "^19.3.0",
76
77
  "signal-exit": "^3.0.7",