@bibobonking/opensol 0.0.0-stage → 3.0.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 (127) hide show
  1. package/.github/workflows/build.yml +25 -0
  2. package/README.md +74 -2
  3. package/dist/assets/index-BmN7pBji.css +1 -0
  4. package/dist/assets/index-DgNDjkOz.js +301 -0
  5. package/dist/favicon.svg +15 -0
  6. package/dist/index.html +55 -0
  7. package/dist/preview-host.html +33 -0
  8. package/index.html +54 -0
  9. package/package.json +43 -4
  10. package/postcss.config.js +6 -0
  11. package/public/favicon.svg +15 -0
  12. package/public/preview-host.html +33 -0
  13. package/server/README.md +75 -0
  14. package/server/daemon.mjs +1082 -0
  15. package/server/lib/checkpoints.mjs +124 -0
  16. package/server/lib/checkpoints.test.mjs +110 -0
  17. package/server/lib/deploy.mjs +436 -0
  18. package/server/lib/http.mjs +237 -0
  19. package/server/lib/plugins.mjs +321 -0
  20. package/server/lib/plugins.test.mjs +140 -0
  21. package/server/lib/providers.mjs +594 -0
  22. package/server/lib/store.mjs +491 -0
  23. package/server/lib/tools.mjs +990 -0
  24. package/server/opensol.mjs +202 -0
  25. package/server/package.json +42 -0
  26. package/shared/plugin-builder.test.mjs +43 -0
  27. package/shared/plugin-package.d.mts +34 -0
  28. package/shared/plugin-package.mjs +197 -0
  29. package/shared/plugin-package.test.mjs +94 -0
  30. package/src/App.tsx +152 -0
  31. package/src/agent/adapters/anthropic.ts +233 -0
  32. package/src/agent/adapters/gemini.ts +225 -0
  33. package/src/agent/adapters/openai-responses.ts +215 -0
  34. package/src/agent/adapters/openai.ts +200 -0
  35. package/src/agent/adapters/shared.ts +96 -0
  36. package/src/agent/approval.ts +20 -0
  37. package/src/agent/compact.ts +234 -0
  38. package/src/agent/index.ts +36 -0
  39. package/src/agent/model.ts +20 -0
  40. package/src/agent/prompt.ts +107 -0
  41. package/src/agent/queue.test.mjs +111 -0
  42. package/src/agent/queue.ts +84 -0
  43. package/src/agent/subagent.ts +109 -0
  44. package/src/agent/tools.ts +541 -0
  45. package/src/components/ChatView.tsx +403 -0
  46. package/src/components/CommandPalette.tsx +416 -0
  47. package/src/components/Composer.tsx +678 -0
  48. package/src/components/ContextBar.tsx +58 -0
  49. package/src/components/DeployModal.tsx +512 -0
  50. package/src/components/ErrorBoundary.tsx +75 -0
  51. package/src/components/HomeDashboard.tsx +189 -0
  52. package/src/components/Logo.tsx +41 -0
  53. package/src/components/Markdown.tsx +232 -0
  54. package/src/components/MessageItem.tsx +655 -0
  55. package/src/components/ModelPicker.tsx +487 -0
  56. package/src/components/PixleSol.tsx +243 -0
  57. package/src/components/PluginBuilder.tsx +285 -0
  58. package/src/components/PluginManager.tsx +341 -0
  59. package/src/components/PreviewPane.tsx +166 -0
  60. package/src/components/SettingsModal.tsx +2052 -0
  61. package/src/components/Sidebar.tsx +504 -0
  62. package/src/components/Splash.tsx +104 -0
  63. package/src/components/ToolCallCard.tsx +177 -0
  64. package/src/components/TopBar.tsx +182 -0
  65. package/src/components/TrajectoryView.tsx +178 -0
  66. package/src/components/WorkspacePanel.tsx +710 -0
  67. package/src/components/ui.tsx +311 -0
  68. package/src/core/kernel.ts +45 -0
  69. package/src/core/plugins/tools/fs.ts +219 -0
  70. package/src/core/plugins/tools/git.ts +83 -0
  71. package/src/core/plugins/tools/grep.ts +98 -0
  72. package/src/core/plugins/tools/shell.ts +88 -0
  73. package/src/core/plugins/tools/subagent.ts +36 -0
  74. package/src/core/plugins/tools/web.ts +97 -0
  75. package/src/core/services/agent-loop.ts +417 -0
  76. package/src/core/services/approval.ts +66 -0
  77. package/src/core/services/compaction.ts +45 -0
  78. package/src/core/services/llm.ts +32 -0
  79. package/src/core/services/sessions.ts +47 -0
  80. package/src/core/services/subagents.ts +15 -0
  81. package/src/core/services/system-prompt.ts +177 -0
  82. package/src/core/services/tools.ts +135 -0
  83. package/src/core/services/ui.ts +109 -0
  84. package/src/core/types.ts +139 -0
  85. package/src/data/providers.ts +125 -0
  86. package/src/data/skills.ts +68 -0
  87. package/src/index.css +776 -0
  88. package/src/lib/boot.ts +94 -0
  89. package/src/lib/context.ts +75 -0
  90. package/src/lib/conversation.test.mjs +64 -0
  91. package/src/lib/conversation.ts +59 -0
  92. package/src/lib/daemon.ts +475 -0
  93. package/src/lib/hooks.ts +33 -0
  94. package/src/lib/permissions.ts +60 -0
  95. package/src/lib/pixlesol.test.mjs +51 -0
  96. package/src/lib/pixlesol.ts +55 -0
  97. package/src/lib/plugin-folder.test.mjs +145 -0
  98. package/src/lib/plugin-folder.ts +96 -0
  99. package/src/lib/reasoning.ts +87 -0
  100. package/src/lib/sse.ts +75 -0
  101. package/src/lib/themes.test.mjs +72 -0
  102. package/src/lib/themes.ts +52 -0
  103. package/src/lib/usage.test.mjs +72 -0
  104. package/src/lib/usage.ts +155 -0
  105. package/src/lib/utils.ts +73 -0
  106. package/src/main.tsx +16 -0
  107. package/src/plugins/api.test.mjs +77 -0
  108. package/src/plugins/api.ts +113 -0
  109. package/src/plugins/bridge.d.mts +1 -0
  110. package/src/plugins/bridge.mjs +26 -0
  111. package/src/plugins/bridge.test.mjs +17 -0
  112. package/src/plugins/index.ts +52 -0
  113. package/src/plugins/preview.test.mjs +18 -0
  114. package/src/plugins/preview.ts +34 -0
  115. package/src/plugins/runtime.ts +194 -0
  116. package/src/plugins/types.ts +40 -0
  117. package/src/store/chats.ts +278 -0
  118. package/src/store/models.ts +120 -0
  119. package/src/store/settings.ts +445 -0
  120. package/src/store/ui.ts +132 -0
  121. package/src/store/workspace.ts +299 -0
  122. package/src/types.ts +337 -0
  123. package/src/vite-env.d.ts +1 -0
  124. package/tailwind.config.js +83 -0
  125. package/tsconfig.json +22 -0
  126. package/tsconfig.tsbuildinfo +1 -0
  127. package/vite.config.ts +19 -0
@@ -0,0 +1,98 @@
1
+ import type { Context } from 'cordis'
2
+ import { executeDaemonTool } from '../../../lib/daemon'
3
+ import type { ToolResult } from '../../types'
4
+
5
+ const MAX_TOOL_OUTPUT = 28_000
6
+
7
+ function truncate(value: string, limit = MAX_TOOL_OUTPUT) {
8
+ if (value.length <= limit) return value
9
+ return `${value.slice(0, limit)}\n… truncated (${value.length - limit} more characters)`
10
+ }
11
+
12
+ function record(value: unknown): Record<string, unknown> {
13
+ return value && typeof value === 'object' ? (value as Record<string, unknown>) : {}
14
+ }
15
+
16
+ function textValue(value: unknown): string {
17
+ return typeof value === 'string' ? value : ''
18
+ }
19
+
20
+ function numberValue(value: unknown): number | undefined {
21
+ return typeof value === 'number' && Number.isFinite(value) ? value : undefined
22
+ }
23
+
24
+ function requireString(args: Record<string, unknown>, key: string): string {
25
+ const value = args[key]
26
+ if (typeof value !== 'string' || !value.trim()) throw new Error(`Missing string argument "${key}".`)
27
+ return value
28
+ }
29
+
30
+ function optionalString(args: Record<string, unknown>, key: string): string | undefined {
31
+ const value = args[key]
32
+ return typeof value === 'string' ? value : undefined
33
+ }
34
+
35
+ function numberArg(args: Record<string, unknown>, key: string, fallback: number): number {
36
+ const value = args[key]
37
+ if (typeof value === 'number' && Number.isFinite(value)) return value
38
+ if (typeof value === 'string' && Number.isFinite(Number(value))) return Number(value)
39
+ return fallback
40
+ }
41
+
42
+ function formatGrep(result: Record<string, unknown>, pattern: string): string {
43
+ const matches = Array.isArray(result.matches) ? result.matches : []
44
+ if (matches.length === 0) return `No matches for ${pattern}.`
45
+ const lines = matches.map((raw) => {
46
+ const match = record(raw)
47
+ return `${textValue(match.file)}:${numberValue(match.line) ?? 0}: ${textValue(match.text)}`
48
+ })
49
+ const suffix = result.truncated === true ? '\n… more matches exist. Narrow the pattern or path.' : ''
50
+ return lines.join('\n') + suffix
51
+ }
52
+
53
+ export const name = 'tool-grep'
54
+ export const inject = ['tools']
55
+
56
+ export function apply(ctx: Context) {
57
+ return ctx.tools.register({
58
+ name: 'grep',
59
+ description: 'Search workspace contents with a regular expression. Returns path, line and matching text.',
60
+ parameters: {
61
+ type: 'object',
62
+ properties: {
63
+ pattern: { type: 'string', description: 'Regular expression to search for.' },
64
+ path: { type: 'string', description: 'File or folder. Defaults to the workspace root.' },
65
+ ignoreCase: { type: 'boolean', description: 'Case-insensitive search. Defaults to false.' },
66
+ maxResults: { type: 'number', description: 'Maximum matches. Defaults to 80.' },
67
+ },
68
+ required: ['pattern'],
69
+ additionalProperties: false,
70
+ },
71
+ allowedInMode: () => true,
72
+ async execute(args, exec): Promise<ToolResult> {
73
+ const pattern = requireString(args, 'pattern')
74
+ const result = record(
75
+ (
76
+ await executeDaemonTool(
77
+ 'grep',
78
+ {
79
+ pattern,
80
+ path: optionalString(args, 'path') ?? '',
81
+ ignoreCase: args.ignoreCase === true,
82
+ },
83
+ exec.mode,
84
+ exec.signal,
85
+ )
86
+ ).result,
87
+ )
88
+ const maxResults = Math.min(Math.max(1, Math.round(numberArg(args, 'maxResults', 80))), 200)
89
+ const matches = Array.isArray(result.matches) ? result.matches.slice(0, maxResults) : []
90
+ const clipped = {
91
+ ...result,
92
+ matches,
93
+ truncated: result.truncated === true || (Array.isArray(result.matches) && result.matches.length > maxResults),
94
+ }
95
+ return { output: truncate(formatGrep(clipped, pattern)), isError: false }
96
+ },
97
+ })
98
+ }
@@ -0,0 +1,88 @@
1
+ import type { Context } from 'cordis'
2
+ import { executeDaemonTool } from '../../../lib/daemon'
3
+ import { useWorkspace } from '../../../store/workspace'
4
+ import type { ToolResult } from '../../types'
5
+
6
+ const MAX_TOOL_OUTPUT = 28_000
7
+
8
+ function truncate(value: string, limit = MAX_TOOL_OUTPUT) {
9
+ if (value.length <= limit) return value
10
+ return `${value.slice(0, limit)}\n… truncated (${value.length - limit} more characters)`
11
+ }
12
+
13
+ function record(value: unknown): Record<string, unknown> {
14
+ return value && typeof value === 'object' ? (value as Record<string, unknown>) : {}
15
+ }
16
+
17
+ function textValue(value: unknown): string {
18
+ return typeof value === 'string' ? value : ''
19
+ }
20
+
21
+ function numberValue(value: unknown): number | undefined {
22
+ return typeof value === 'number' && Number.isFinite(value) ? value : undefined
23
+ }
24
+
25
+ function requireString(args: Record<string, unknown>, key: string): string {
26
+ const value = args[key]
27
+ if (typeof value !== 'string' || !value.trim()) throw new Error(`Missing string argument "${key}".`)
28
+ return value
29
+ }
30
+
31
+ function numberArg(args: Record<string, unknown>, key: string, fallback: number): number {
32
+ const value = args[key]
33
+ if (typeof value === 'number' && Number.isFinite(value)) return value
34
+ if (typeof value === 'string' && Number.isFinite(Number(value))) return Number(value)
35
+ return fallback
36
+ }
37
+
38
+ function formatShell(result: Record<string, unknown>, command: string): string {
39
+ const parts = [`$ ${command}`, textValue(result.stdout)]
40
+ if (textValue(result.stderr)) parts.push(`stderr:\n${textValue(result.stderr)}`)
41
+ const code = numberValue(result.code)
42
+ if (code !== undefined && code !== 0) parts.push(`exit code: ${code}`)
43
+ if (result.timedOut === true) parts.push('The command timed out.')
44
+ return parts.filter((part) => part !== '').join('\n')
45
+ }
46
+
47
+ async function refreshTree() {
48
+ await useWorkspace.getState().refresh({ treeOnly: true }).catch(() => undefined)
49
+ }
50
+
51
+ export const name = 'tool-shell'
52
+ export const inject = ['tools']
53
+
54
+ export function apply(ctx: Context) {
55
+ return ctx.tools.register({
56
+ name: 'shell',
57
+ description:
58
+ 'Run a shell command in the workspace root. The working directory resets between calls. Prefer dedicated file and git tools for edits. In Plan mode only read-only commands are accepted.',
59
+ parameters: {
60
+ type: 'object',
61
+ properties: {
62
+ command: { type: 'string', description: 'The shell command to run.' },
63
+ timeoutMs: { type: 'number', description: 'Timeout in milliseconds (max 600000).' },
64
+ },
65
+ required: ['command'],
66
+ additionalProperties: false,
67
+ },
68
+ allowedInMode: () => true,
69
+ async execute(args, exec): Promise<ToolResult> {
70
+ const command = requireString(args, 'command')
71
+ const result = record(
72
+ (
73
+ await executeDaemonTool(
74
+ 'shell',
75
+ { command, timeoutMs: numberArg(args, 'timeoutMs', 120_000) },
76
+ exec.mode,
77
+ exec.signal,
78
+ )
79
+ ).result,
80
+ )
81
+ void refreshTree()
82
+ return {
83
+ output: truncate(formatShell(result, command)),
84
+ isError: (numberValue(result.code) ?? 0) !== 0 || result.timedOut === true,
85
+ }
86
+ },
87
+ })
88
+ }
@@ -0,0 +1,36 @@
1
+ import type { Context } from 'cordis'
2
+ import type { ToolResult } from '../../types'
3
+
4
+ const MAX_TOOL_OUTPUT = 28_000
5
+
6
+ function truncate(value: string, limit = MAX_TOOL_OUTPUT) {
7
+ if (value.length <= limit) return value
8
+ return `${value.slice(0, limit)}\n… truncated (${value.length - limit} more characters)`
9
+ }
10
+
11
+ export const name = 'tool-subagent'
12
+ export const inject = ['tools', 'subagents']
13
+
14
+ export function apply(ctx: Context) {
15
+ return ctx.tools.register({
16
+ name: 'subagent',
17
+ description:
18
+ 'Delegate a focused investigation to a read-only subagent. Use it to explore a large area, trace a bug or verify assumptions without changing files. The subagent returns a concise report.',
19
+ parameters: {
20
+ type: 'object',
21
+ properties: {
22
+ task: { type: 'string', description: 'The specific question or investigation to perform.' },
23
+ context: { type: 'string', description: 'Optional context, constraints or files to start from.' },
24
+ },
25
+ required: ['task'],
26
+ additionalProperties: false,
27
+ },
28
+ allowedInMode: () => true,
29
+ async execute(args, exec): Promise<ToolResult> {
30
+ const task = typeof args.task === 'string' ? args.task : ''
31
+ const context = typeof args.context === 'string' ? args.context : ''
32
+ const output = await ctx.subagents.run(task, context, exec.signal)
33
+ return { output: truncate(output), isError: false }
34
+ },
35
+ })
36
+ }
@@ -0,0 +1,97 @@
1
+ import type { Context } from 'cordis'
2
+ import { executeDaemonTool } from '../../../lib/daemon'
3
+ import type { ToolResult } from '../../types'
4
+
5
+ const MAX_TOOL_OUTPUT = 28_000
6
+
7
+ function truncate(value: string, limit = MAX_TOOL_OUTPUT) {
8
+ if (value.length <= limit) return value
9
+ return `${value.slice(0, limit)}\n… truncated (${value.length - limit} more characters)`
10
+ }
11
+
12
+ function record(value: unknown): Record<string, unknown> {
13
+ return value && typeof value === 'object' ? (value as Record<string, unknown>) : {}
14
+ }
15
+
16
+ function textValue(value: unknown): string {
17
+ return typeof value === 'string' ? value : ''
18
+ }
19
+
20
+ function requireString(args: Record<string, unknown>, key: string): string {
21
+ const value = args[key]
22
+ if (typeof value !== 'string' || !value.trim()) throw new Error(`Missing string argument "${key}".`)
23
+ return value
24
+ }
25
+
26
+ export const name = 'tool-web'
27
+ export const inject = ['tools']
28
+
29
+ export function apply(ctx: Context) {
30
+ const unregisters = [
31
+ ctx.tools.register({
32
+ name: 'web_search',
33
+ description: 'Search the web for current information. Returns titles, URLs and snippets. Cite URLs you use.',
34
+ parameters: {
35
+ type: 'object',
36
+ properties: {
37
+ query: { type: 'string', description: 'Search query.' },
38
+ },
39
+ required: ['query'],
40
+ additionalProperties: false,
41
+ },
42
+ allowedInMode: () => true,
43
+ async execute(args, exec): Promise<ToolResult> {
44
+ const query = requireString(args, 'query')
45
+ const result = record(
46
+ (
47
+ await executeDaemonTool(
48
+ 'web_search',
49
+ { query, provider: exec.settings.searchProvider },
50
+ exec.mode,
51
+ exec.signal,
52
+ )
53
+ ).result,
54
+ )
55
+ const results = Array.isArray(result.results) ? result.results : []
56
+ if (results.length === 0) return { output: 'No results found.', isError: false }
57
+ const formatted = results
58
+ .map((raw, index) => {
59
+ const item = record(raw)
60
+ return `${index + 1}. ${textValue(item.title)}\n ${textValue(item.url)}${textValue(item.snippet) ? `\n ${textValue(item.snippet)}` : ''}`
61
+ })
62
+ .join('\n\n')
63
+ return {
64
+ output: truncate(`Search: ${query}\nProvider: ${textValue(result.provider) || 'web'}\n\n${formatted}`),
65
+ isError: false,
66
+ }
67
+ },
68
+ }),
69
+
70
+ ctx.tools.register({
71
+ name: 'read_url',
72
+ description: 'Read a public web page as plain text.',
73
+ parameters: {
74
+ type: 'object',
75
+ properties: {
76
+ url: { type: 'string', description: 'The http(s) URL to read.' },
77
+ },
78
+ required: ['url'],
79
+ additionalProperties: false,
80
+ },
81
+ allowedInMode: () => true,
82
+ async execute(args, exec): Promise<ToolResult> {
83
+ const url = requireString(args, 'url')
84
+ const result = record((await executeDaemonTool('read_url', { url }, exec.mode, exec.signal)).result)
85
+ const title = textValue(result.title)
86
+ return {
87
+ output: truncate(
88
+ `Source: ${textValue(result.url) || url}${title ? `\nTitle: ${title}` : ''}\n\n${textValue(result.content)}${result.truncated === true ? '\n\n… page truncated.' : ''}`,
89
+ ),
90
+ isError: false,
91
+ }
92
+ },
93
+ }),
94
+ ]
95
+
96
+ return () => unregisters.forEach((fn) => fn())
97
+ }