@bibobonking/opensol 3.1.4 → 3.1.6

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 (73) hide show
  1. package/CHANGELOG.md +14 -12
  2. package/LICENSE +21 -0
  3. package/desktop/src-tauri/icons/128x128.png +0 -0
  4. package/desktop/src-tauri/icons/128x128@2x.png +0 -0
  5. package/desktop/src-tauri/icons/32x32.png +0 -0
  6. package/desktop/src-tauri/icons/64x64.png +0 -0
  7. package/desktop/src-tauri/icons/Square107x107Logo.png +0 -0
  8. package/desktop/src-tauri/icons/Square142x142Logo.png +0 -0
  9. package/desktop/src-tauri/icons/Square150x150Logo.png +0 -0
  10. package/desktop/src-tauri/icons/Square284x284Logo.png +0 -0
  11. package/desktop/src-tauri/icons/Square30x30Logo.png +0 -0
  12. package/desktop/src-tauri/icons/Square310x310Logo.png +0 -0
  13. package/desktop/src-tauri/icons/Square44x44Logo.png +0 -0
  14. package/desktop/src-tauri/icons/Square71x71Logo.png +0 -0
  15. package/desktop/src-tauri/icons/Square89x89Logo.png +0 -0
  16. package/desktop/src-tauri/icons/StoreLogo.png +0 -0
  17. package/desktop/src-tauri/icons/icon.icns +0 -0
  18. package/desktop/src-tauri/icons/icon.ico +0 -0
  19. package/desktop/src-tauri/icons/icon.png +0 -0
  20. package/dist/assets/index-BUVnnhRH.js +281 -0
  21. package/dist/assets/index-CxNPLBg7.css +1 -0
  22. package/dist/favicon.svg +18 -15
  23. package/dist/index.html +2 -2
  24. package/package.json +6 -3
  25. package/public/favicon.svg +18 -15
  26. package/server/daemon.mjs +66 -49
  27. package/server/lib/deploy.mjs +222 -121
  28. package/server/lib/providers.mjs +23 -1
  29. package/server/lib/skills.mjs +385 -100
  30. package/server/lib/tools.mjs +0 -40
  31. package/src/agent/adapters/openai-responses.ts +1 -5
  32. package/src/agent/adapters/openai.ts +4 -11
  33. package/src/agent/prompt.ts +7 -14
  34. package/src/agent/subagent.ts +3 -14
  35. package/src/agent/tools.ts +2 -0
  36. package/src/components/ChatView.tsx +2 -1
  37. package/src/components/Composer.tsx +2 -1
  38. package/src/components/ContextBar.tsx +10 -0
  39. package/src/components/DeployModal.tsx +266 -252
  40. package/src/components/FileIcon.tsx +391 -99
  41. package/src/components/HomeDashboard.tsx +5 -10
  42. package/src/components/Logo.tsx +9 -11
  43. package/src/components/ModelDeck.tsx +8 -11
  44. package/src/components/SettingsModal.tsx +160 -122
  45. package/src/components/Sidebar.tsx +4 -3
  46. package/src/components/SystemPromptButton.tsx +198 -0
  47. package/src/components/ToolCallCard.tsx +2 -0
  48. package/src/components/TopBar.tsx +2 -19
  49. package/src/components/WorkspacePanel.tsx +76 -93
  50. package/src/core/plugins/tools/skill.ts +19 -76
  51. package/src/core/services/agent-loop.ts +16 -31
  52. package/src/core/services/subagents.ts +2 -2
  53. package/src/core/services/system-prompt.ts +17 -23
  54. package/src/core/services/tools.ts +0 -1
  55. package/src/lib/boot.ts +2 -0
  56. package/src/lib/credits.ts +33 -0
  57. package/src/lib/daemon.ts +40 -19
  58. package/src/lib/permissions.ts +1 -1
  59. package/src/lib/retry.test.mjs +40 -12
  60. package/src/lib/retry.ts +41 -8
  61. package/src/lib/system-prompt.ts +23 -0
  62. package/src/lib/usage.ts +8 -13
  63. package/src/store/chats.ts +0 -1
  64. package/src/store/settings.ts +12 -44
  65. package/src/store/skills.ts +79 -0
  66. package/src/store/ui.ts +3 -1
  67. package/src/types.ts +30 -15
  68. package/tsconfig.tsbuildinfo +1 -1
  69. package/dist/assets/index-B1pQC9Fe.js +0 -288
  70. package/dist/assets/index-qLr4aZVC.css +0 -1
  71. package/src/components/SystemPromptModal.tsx +0 -168
  72. package/src/lib/skills.test.mjs +0 -22
  73. package/src/lib/skills.ts +0 -93
@@ -2,6 +2,7 @@ import type { AgentMode, AppSettings } from '../types'
2
2
  import { activeModelId } from '../store/settings'
3
3
  import { useWorkspace } from '../store/workspace'
4
4
  import { kernel } from '../core/kernel'
5
+ import { skillCatalogPrompt } from '../store/skills'
5
6
 
6
7
  function workspaceContext(): string {
7
8
  const state = useWorkspace.getState()
@@ -26,22 +27,14 @@ function workspaceContext(): string {
26
27
  .join('\n')
27
28
  }
28
29
 
29
- export function buildSystemPrompt(settings: AppSettings, mode: AgentMode = settings.mode): string {
30
+ export function buildSystemPrompt(settings: AppSettings, mode: AgentMode = settings.mode, sessionId?: string): string {
30
31
  if (kernel.systemPrompt) {
31
- return kernel.systemPrompt.assemble(settings, mode)
32
+ return kernel.systemPrompt.assemble(settings, mode, sessionId)
32
33
  }
33
34
  const chunks: string[] = [settings.systemPrompt.trim()]
34
35
 
35
- const skills = settings.skills.filter((skill) => skill.enabled)
36
- if (skills.length > 0) {
37
- chunks.push(
38
- [
39
- '## Available skills',
40
- 'Call the `skill` tool to load the full instructions (and run scripts from the skill folder) when a task matches one of these. Do not guess a skill’s contents.',
41
- ...skills.map((skill) => `- ${skill.name} (${skill.id}): ${skill.description}`),
42
- ].join('\n'),
43
- )
44
- }
36
+ const catalog = skillCatalogPrompt()
37
+ if (catalog && settings.tools.skill?.enabled !== false) chunks.push(catalog)
45
38
 
46
39
  chunks.push(
47
40
  [
@@ -89,9 +82,9 @@ export function buildSystemPrompt(settings: AppSettings, mode: AgentMode = setti
89
82
  return chunks.filter(Boolean).join('\n\n')
90
83
  }
91
84
 
92
- export function buildSubagentPrompt(settings: AppSettings): string {
85
+ export function buildSubagentPrompt(settings: AppSettings, sessionId?: string): string {
93
86
  return [
94
- buildSystemPrompt(settings, 'plan'),
87
+ buildSystemPrompt(settings, 'plan', sessionId),
95
88
  [
96
89
  '## Subagent',
97
90
  'You are a focused read-only investigator. Work only on the assigned task.',
@@ -11,8 +11,6 @@ import { streamModel } from './model'
11
11
  import { buildSubagentPrompt } from './prompt'
12
12
  import { executeTool } from './tools'
13
13
 
14
- const MAX_SUBAGENT_ROUNDS = 6
15
-
16
14
  function id(prefix: string) {
17
15
  const raw =
18
16
  typeof crypto !== 'undefined' && 'randomUUID' in crypto
@@ -33,6 +31,7 @@ export async function runSubagent(
33
31
  task: string,
34
32
  context: string,
35
33
  signal: AbortSignal,
34
+ sessionId?: string,
36
35
  ): Promise<string> {
37
36
  const cleanTask = task.trim()
38
37
  if (!cleanTask) return 'The subagent was not given a task.'
@@ -40,7 +39,7 @@ export async function runSubagent(
40
39
 
41
40
  const settings = useSettings.getState()
42
41
  const model = findModel(settings.provider, settings.model)
43
- const system = buildSubagentPrompt(settings)
42
+ const system = buildSubagentPrompt(settings, sessionId)
44
43
  const now = Date.now()
45
44
  const first: UserMessage = {
46
45
  id: id('sub'),
@@ -55,9 +54,8 @@ export async function runSubagent(
55
54
  createdAt: now,
56
55
  }
57
56
  const history: Message[] = [first]
58
- let lastContent = ''
59
57
 
60
- for (let round = 0; round < MAX_SUBAGENT_ROUNDS; round += 1) {
58
+ for (let round = 0; ; round += 1) {
61
59
  if (signal.aborted) throw abortError()
62
60
  const result = await streamModel({
63
61
  settings,
@@ -68,8 +66,6 @@ export async function runSubagent(
68
66
  signal,
69
67
  handlers: EMPTY_HANDLERS,
70
68
  })
71
- if (result.content.trim()) lastContent = result.content.trim()
72
-
73
69
  const assistant: AssistantMessage = {
74
70
  id: id('submsg'),
75
71
  role: 'assistant',
@@ -99,11 +95,4 @@ export async function runSubagent(
99
95
  history.push(toolMessage)
100
96
  }
101
97
  }
102
-
103
- return [
104
- lastContent,
105
- `The subagent stopped after ${MAX_SUBAGENT_ROUNDS} investigation rounds. Continue with the evidence above or ask a narrower question.`,
106
- ]
107
- .filter(Boolean)
108
- .join('\n\n')
109
98
  }
@@ -538,6 +538,8 @@ export function toolSummary(name: ToolName, args: Record<string, unknown>): stri
538
538
  ? args.action
539
539
  : name === 'subagent'
540
540
  ? args.task
541
+ : name === 'skill'
542
+ ? [args.name, args.file].filter(Boolean).join(' / ')
541
543
  : (args.path ?? '')
542
544
  return typeof value === 'string' ? value : ''
543
545
  }
@@ -1,3 +1,4 @@
1
+ import { useSkills } from '../store/skills'
1
2
  import { useEffect, useRef, useState } from 'react'
2
3
  import { ArrowUp, Bug, FlaskConical, FolderSearch, Gamepad2, Globe2, KeyRound, LayoutTemplate, ListChecks, Sparkles } from 'lucide-react'
3
4
  import type { Attachment } from '../types'
@@ -86,7 +87,7 @@ export function ChatView() {
86
87
  const lastAssistantId = [...messages].reverse().find((message) => message.role === 'assistant')?.id
87
88
  const connected = hasCredentials(settings)
88
89
  const selectedModel = activeModelId(settings)
89
- const activeSkills = settings.skills.filter((skill) => skill.enabled).length
90
+ const activeSkills = useSkills((state) => state.skills.filter((skill) => skill.enabled).length)
90
91
  const turns = groupConversationTurns(messages)
91
92
  const context = getContextUsage(messages, activeModel)
92
93
  const mood = pixleMood(messages, streaming)
@@ -24,6 +24,7 @@ import {
24
24
  X,
25
25
  } from 'lucide-react'
26
26
  import type { Attachment } from '../types'
27
+ import { useSkills } from '../store/skills'
27
28
  import { activeModelId, hasCredentials, useSettings } from '../store/settings'
28
29
  import { useActiveModel, useProviderModels } from '../store/models'
29
30
  import { useUi } from '../store/ui'
@@ -124,7 +125,7 @@ export function Composer({
124
125
  const activeModel = useActiveModel()
125
126
  const connected = hasCredentials(settings)
126
127
  const efforts = effortOptions(activeModel)
127
- const activeSkills = settings.skills.filter((skill) => skill.enabled).length
128
+ const activeSkills = useSkills((state) => state.skills.filter((skill) => skill.enabled).length)
128
129
  const permission = permissionMode(settings.tools)
129
130
  const modelLabel = !connected
130
131
  ? 'No provider'
@@ -10,6 +10,7 @@ import { useMemo } from 'react'
10
10
  import type { ModelInfo, Message } from '../types'
11
11
  import { getContextUsage, AUTO_COMPACT_THRESHOLD } from '../lib/context'
12
12
  import { isCompacting } from '../agent/compact'
13
+ import { formatCredits, sumCost } from '../lib/credits'
13
14
  import { cn } from '../lib/utils'
14
15
 
15
16
  interface ContextBarProps {
@@ -26,6 +27,7 @@ function formatTokens(n: number): string {
26
27
 
27
28
  export function ContextBar({ messages, model, sessionId }: ContextBarProps) {
28
29
  const usage = useMemo(() => getContextUsage(messages, model), [messages, model])
30
+ const spent = useMemo(() => sumCost(messages), [messages])
29
31
  const compacting = sessionId ? isCompacting(sessionId) : false
30
32
 
31
33
  if (messages.length === 0) return null
@@ -51,6 +53,14 @@ export function ContextBar({ messages, model, sessionId }: ContextBarProps) {
51
53
  <span className="text-faint">/ {formatTokens(limit)} tokens</span>
52
54
  <span className="text-faint">·</span>
53
55
  <span>{pct}%</span>
56
+ {spent > 0 && (
57
+ <>
58
+ <span className="text-faint">·</span>
59
+ <span className="text-ink/80" title="Credits spent in this session (provider-reported, or tokens × model price)">
60
+ {formatCredits(spent)}
61
+ </span>
62
+ </>
63
+ )}
54
64
  </>
55
65
  )}
56
66
  </div>