@myrialabs/clopen 0.0.8 → 0.1.1
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.
- package/backend/index.ts +9 -0
- package/backend/lib/chat/stream-manager.ts +130 -10
- package/backend/lib/database/queries/message-queries.ts +47 -0
- package/backend/lib/engine/adapters/claude/stream.ts +65 -1
- package/backend/lib/engine/adapters/opencode/message-converter.ts +35 -77
- package/backend/lib/engine/types.ts +6 -0
- package/backend/lib/files/file-operations.ts +2 -2
- package/backend/lib/files/file-reading.ts +2 -2
- package/backend/lib/files/path-browsing.ts +2 -2
- package/backend/lib/terminal/pty-session-manager.ts +1 -1
- package/backend/lib/terminal/shell-utils.ts +4 -4
- package/backend/ws/chat/background.ts +3 -0
- package/backend/ws/chat/stream.ts +43 -1
- package/bin/clopen.ts +10 -0
- package/bun.lock +259 -381
- package/frontend/lib/components/chat/ChatInterface.svelte +8 -1
- package/frontend/lib/components/chat/formatters/MessageFormatter.svelte +20 -0
- package/frontend/lib/components/chat/formatters/TextMessage.svelte +3 -15
- package/frontend/lib/components/chat/formatters/Tools.svelte +15 -8
- package/frontend/lib/components/chat/input/ChatInput.svelte +70 -21
- package/frontend/lib/components/chat/input/components/LoadingIndicator.svelte +23 -11
- package/frontend/lib/components/chat/input/composables/use-chat-actions.svelte.ts +1 -1
- package/frontend/lib/components/chat/message/ChatMessage.svelte +13 -1
- package/frontend/lib/components/chat/message/ChatMessages.svelte +2 -2
- package/frontend/lib/components/chat/message/DateSeparator.svelte +1 -1
- package/frontend/lib/components/chat/message/MessageBubble.svelte +2 -2
- package/frontend/lib/components/chat/message/MessageHeader.svelte +14 -12
- package/frontend/lib/components/chat/tools/AgentTool.svelte +95 -0
- package/frontend/lib/components/chat/tools/AskUserQuestionTool.svelte +396 -0
- package/frontend/lib/components/chat/tools/BashTool.svelte +9 -4
- package/frontend/lib/components/chat/tools/EnterPlanModeTool.svelte +24 -0
- package/frontend/lib/components/chat/tools/ExitPlanModeTool.svelte +4 -7
- package/frontend/lib/components/chat/tools/{KillShellTool.svelte → TaskStopTool.svelte} +6 -6
- package/frontend/lib/components/chat/tools/index.ts +5 -2
- package/frontend/lib/components/checkpoint/TimelineModal.svelte +7 -2
- package/frontend/lib/components/history/HistoryModal.svelte +13 -5
- package/frontend/lib/components/workspace/DesktopNavigator.svelte +2 -1
- package/frontend/lib/components/workspace/MobileNavigator.svelte +2 -1
- package/frontend/lib/services/chat/chat.service.ts +146 -12
- package/frontend/lib/stores/core/app.svelte.ts +77 -0
- package/frontend/lib/utils/chat/message-grouper.ts +94 -12
- package/frontend/lib/utils/chat/message-processor.ts +37 -4
- package/frontend/lib/utils/chat/tool-handler.ts +96 -5
- package/package.json +4 -4
- package/shared/constants/engines.ts +1 -1
- package/shared/types/database/schema.ts +1 -0
- package/shared/types/messaging/index.ts +15 -13
- package/shared/types/messaging/tool.ts +185 -361
- package/shared/utils/message-formatter.ts +1 -0
|
@@ -7,31 +7,44 @@ import type {
|
|
|
7
7
|
ToolGroup,
|
|
8
8
|
BackgroundBashData
|
|
9
9
|
} from './message-grouper';
|
|
10
|
+
import type { SDKMessageFormatter } from '$shared/types/database/schema';
|
|
11
|
+
import type { SubAgentActivity } from '$shared/types/messaging';
|
|
10
12
|
|
|
11
|
-
// Extended ToolUse with embedded result
|
|
13
|
+
// Extended ToolUse with embedded result and metadata
|
|
12
14
|
export interface ToolUseWithResult {
|
|
13
15
|
type: 'tool_use';
|
|
14
16
|
id: string;
|
|
15
17
|
name: string;
|
|
16
18
|
input: any;
|
|
17
19
|
$result?: any;
|
|
20
|
+
$subMessages?: SubAgentActivity[];
|
|
21
|
+
metadata?: Record<string, unknown>;
|
|
18
22
|
}
|
|
19
23
|
|
|
20
24
|
// Process a tool message with embedded results
|
|
21
25
|
export function processToolMessage(
|
|
22
26
|
message: ProcessedMessage,
|
|
23
27
|
toolUseMap: Map<string, ToolGroup>,
|
|
24
|
-
backgroundBashMap: Map<string, BackgroundBashData
|
|
28
|
+
backgroundBashMap: Map<string, BackgroundBashData>,
|
|
29
|
+
subAgentMap: Map<string, SDKMessageFormatter[]>
|
|
25
30
|
): ProcessedMessage {
|
|
26
31
|
const messageAny = message as any;
|
|
27
32
|
const content = messageAny.message?.content ?
|
|
28
33
|
(Array.isArray(messageAny.message.content) ? messageAny.message.content : [messageAny.message.content]) : [];
|
|
29
34
|
|
|
35
|
+
// Check if parent message is marked as interrupted
|
|
36
|
+
const isInterrupted = !!(messageAny.metadata?.interrupted);
|
|
37
|
+
|
|
30
38
|
// Create modified content with embedded tool_result in tool_use objects
|
|
31
39
|
const modifiedContent = content
|
|
32
40
|
.map((item: any): any => {
|
|
33
41
|
if (typeof item === 'object' && item && 'type' in item && item.type === 'tool_use') {
|
|
34
|
-
|
|
42
|
+
const processed = processToolUse(item, toolUseMap, backgroundBashMap, subAgentMap);
|
|
43
|
+
// Propagate message-level interrupted flag to ALL tool_use blocks
|
|
44
|
+
if (processed && isInterrupted) {
|
|
45
|
+
return { ...processed, metadata: { ...processed.metadata, interrupted: true } };
|
|
46
|
+
}
|
|
47
|
+
return processed;
|
|
35
48
|
}
|
|
36
49
|
return item;
|
|
37
50
|
})
|
|
@@ -51,7 +64,8 @@ export function processToolMessage(
|
|
|
51
64
|
function processToolUse(
|
|
52
65
|
item: any,
|
|
53
66
|
toolUseMap: Map<string, ToolGroup>,
|
|
54
|
-
backgroundBashMap: Map<string, BackgroundBashData
|
|
67
|
+
backgroundBashMap: Map<string, BackgroundBashData>,
|
|
68
|
+
subAgentMap: Map<string, SDKMessageFormatter[]>
|
|
55
69
|
): ToolUseWithResult | null {
|
|
56
70
|
// Hide certain tools completely
|
|
57
71
|
if (shouldHideTool(item.name)) {
|
|
@@ -66,6 +80,11 @@ function processToolUse(
|
|
|
66
80
|
return handleBackgroundBash(item, toolUseMap, backgroundBashMap);
|
|
67
81
|
}
|
|
68
82
|
|
|
83
|
+
// Special handling for Agent tool — embed sub-agent activities
|
|
84
|
+
if (item.name === 'Agent' && item.id && subAgentMap.has(item.id)) {
|
|
85
|
+
return handleAgentTool(item, toolUseMap, subAgentMap);
|
|
86
|
+
}
|
|
87
|
+
|
|
69
88
|
// Regular tool handling
|
|
70
89
|
if (item.id && item.name && shouldEmbedResult(item.name) && toolUseMap.has(item.id)) {
|
|
71
90
|
return handleRegularTool(item, toolUseMap);
|
|
@@ -74,6 +93,78 @@ function processToolUse(
|
|
|
74
93
|
return item;
|
|
75
94
|
}
|
|
76
95
|
|
|
96
|
+
// Handle Agent tool — process sub-agent messages into activities
|
|
97
|
+
function handleAgentTool(
|
|
98
|
+
item: any,
|
|
99
|
+
toolUseMap: Map<string, ToolGroup>,
|
|
100
|
+
subAgentMap: Map<string, SDKMessageFormatter[]>
|
|
101
|
+
): ToolUseWithResult {
|
|
102
|
+
const subMessages = subAgentMap.get(item.id) || [];
|
|
103
|
+
const activities = processSubAgentMessages(subMessages);
|
|
104
|
+
|
|
105
|
+
// Also embed the $result if available
|
|
106
|
+
let result: any = undefined;
|
|
107
|
+
if (item.id && toolUseMap.has(item.id)) {
|
|
108
|
+
const group = toolUseMap.get(item.id);
|
|
109
|
+
if (group?.toolResultMessage) {
|
|
110
|
+
const resultMessage = group.toolResultMessage as any;
|
|
111
|
+
const resultContent = resultMessage.message ?
|
|
112
|
+
(Array.isArray(resultMessage.message.content) ? resultMessage.message.content : [resultMessage.message.content]) : [];
|
|
113
|
+
result = findToolResult(resultContent, item.id);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
return {
|
|
118
|
+
...item,
|
|
119
|
+
...(result ? { $result: result } : {}),
|
|
120
|
+
...(activities.length > 0 ? { $subMessages: activities } : {})
|
|
121
|
+
} as ToolUseWithResult;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// Process sub-agent messages into a flat activity list
|
|
125
|
+
function processSubAgentMessages(messages: SDKMessageFormatter[]): SubAgentActivity[] {
|
|
126
|
+
const activities: SubAgentActivity[] = [];
|
|
127
|
+
const toolResultMap = new Map<string, any>();
|
|
128
|
+
|
|
129
|
+
// First pass: collect all tool_results from user messages
|
|
130
|
+
for (const msg of messages) {
|
|
131
|
+
if (msg.type === 'user' && 'message' in msg && msg.message?.content) {
|
|
132
|
+
const content = Array.isArray(msg.message.content) ? msg.message.content : [msg.message.content];
|
|
133
|
+
for (const item of content) {
|
|
134
|
+
if (typeof item === 'object' && item && item.type === 'tool_result' && item.tool_use_id) {
|
|
135
|
+
toolResultMap.set(item.tool_use_id, item);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// Second pass: build activity list from assistant messages
|
|
142
|
+
for (const msg of messages) {
|
|
143
|
+
if (msg.type === 'assistant' && 'message' in msg && msg.message?.content) {
|
|
144
|
+
const content = Array.isArray(msg.message.content) ? msg.message.content : [msg.message.content];
|
|
145
|
+
for (const item of content) {
|
|
146
|
+
if (typeof item === 'object' && item) {
|
|
147
|
+
if (item.type === 'tool_use') {
|
|
148
|
+
activities.push({
|
|
149
|
+
type: 'tool_use',
|
|
150
|
+
toolName: item.name,
|
|
151
|
+
toolInput: item.input,
|
|
152
|
+
toolResult: toolResultMap.get(item.id) || undefined
|
|
153
|
+
});
|
|
154
|
+
} else if (item.type === 'text' && item.text?.trim()) {
|
|
155
|
+
activities.push({
|
|
156
|
+
type: 'text',
|
|
157
|
+
text: item.text
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
return activities;
|
|
166
|
+
}
|
|
167
|
+
|
|
77
168
|
// Handle background bash commands
|
|
78
169
|
function handleBackgroundBash(
|
|
79
170
|
item: any,
|
|
@@ -158,4 +249,4 @@ function findToolResult(
|
|
|
158
249
|
'tool_use_id' in resultItem &&
|
|
159
250
|
resultItem.tool_use_id === toolUseId
|
|
160
251
|
);
|
|
161
|
-
}
|
|
252
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@myrialabs/clopen",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "All-in-one web workspace for Claude Code & OpenCode — chat, terminal, git, browser preview, checkpoints, and real-time collaboration",
|
|
5
5
|
"author": "Myria Labs",
|
|
6
6
|
"license": "MIT",
|
|
@@ -74,14 +74,14 @@
|
|
|
74
74
|
"vite": "^7.0.4"
|
|
75
75
|
},
|
|
76
76
|
"dependencies": {
|
|
77
|
-
"@anthropic-ai/claude-agent-sdk": "^0.2.
|
|
78
|
-
"@anthropic-ai/sdk": "^0.
|
|
77
|
+
"@anthropic-ai/claude-agent-sdk": "^0.2.63",
|
|
78
|
+
"@anthropic-ai/sdk": "^0.78.0",
|
|
79
79
|
"@elysiajs/cors": "^1.4.0",
|
|
80
80
|
"@iconify-json/lucide": "^1.2.57",
|
|
81
81
|
"@iconify-json/material-icon-theme": "^1.2.16",
|
|
82
82
|
"@modelcontextprotocol/sdk": "^1.26.0",
|
|
83
83
|
"@monaco-editor/loader": "^1.5.0",
|
|
84
|
-
"@opencode-ai/sdk": "^1.2.
|
|
84
|
+
"@opencode-ai/sdk": "^1.2.15",
|
|
85
85
|
"@tailwindcss/typography": "^0.5.16",
|
|
86
86
|
"@types/marked": "^5.0.2",
|
|
87
87
|
"@types/node": "^24.0.14",
|
|
@@ -52,7 +52,7 @@ export const CLAUDE_CODE_MODELS: EngineModel[] = [
|
|
|
52
52
|
id: 'claude-code:sonnet',
|
|
53
53
|
engine: 'claude-code',
|
|
54
54
|
modelId: 'sonnet',
|
|
55
|
-
name: 'Sonnet 4.
|
|
55
|
+
name: 'Sonnet 4.6',
|
|
56
56
|
provider: 'anthropic',
|
|
57
57
|
description: 'High-performance model with excellent coding capabilities and extended thinking',
|
|
58
58
|
capabilities: ['Reasoning', 'Attachments'],
|
|
@@ -69,6 +69,7 @@ export type SDKMessageFormatter = EngineSDKMessage & {
|
|
|
69
69
|
parent_message_id?: string | null; // Git-like parent pointer
|
|
70
70
|
engine?: string; // Engine type that produced this message (claude-code, opencode)
|
|
71
71
|
reasoning?: boolean; // Whether this message is a reasoning/thinking message
|
|
72
|
+
interrupted?: boolean; // Whether the stream ended before all tools got results
|
|
72
73
|
};
|
|
73
74
|
};
|
|
74
75
|
|
|
@@ -65,6 +65,7 @@ import type { SDKUserMessage as _SDKUserMessage } from '@anthropic-ai/claude-age
|
|
|
65
65
|
export type EngineSDKMessage = SDKMessage & {
|
|
66
66
|
metadata?: {
|
|
67
67
|
reasoning?: boolean;
|
|
68
|
+
interrupted?: boolean;
|
|
68
69
|
};
|
|
69
70
|
};
|
|
70
71
|
|
|
@@ -113,7 +114,7 @@ export type {
|
|
|
113
114
|
ExitPlanModeToolInput,
|
|
114
115
|
GlobToolInput,
|
|
115
116
|
GrepToolInput,
|
|
116
|
-
|
|
117
|
+
TaskStopToolInput,
|
|
117
118
|
ListMcpResourcesToolInput,
|
|
118
119
|
NotebookEditToolInput,
|
|
119
120
|
ReadMcpResourceToolInput,
|
|
@@ -123,30 +124,31 @@ export type {
|
|
|
123
124
|
WebFetchToolInput,
|
|
124
125
|
WebSearchToolInput,
|
|
125
126
|
WriteToolInput,
|
|
127
|
+
AskUserQuestionToolInput,
|
|
128
|
+
ConfigToolInput,
|
|
129
|
+
EnterWorktreeToolInput,
|
|
130
|
+
AgentToolInput,
|
|
131
|
+
EnterPlanModeToolInput,
|
|
132
|
+
SubAgentActivity,
|
|
126
133
|
ToolInput,
|
|
127
134
|
|
|
128
|
-
// Tool output types
|
|
135
|
+
// Tool output types (from SDK)
|
|
129
136
|
TaskOutput,
|
|
137
|
+
AskUserQuestionOutput,
|
|
130
138
|
BashOutput,
|
|
131
|
-
|
|
139
|
+
ConfigOutput,
|
|
140
|
+
EnterWorktreeOutput,
|
|
132
141
|
EditOutput,
|
|
133
|
-
|
|
134
|
-
ImageFileOutput,
|
|
135
|
-
PDFFileOutput,
|
|
136
|
-
NotebookFileOutput,
|
|
142
|
+
ExitPlanModeOutput,
|
|
137
143
|
ReadOutput,
|
|
144
|
+
WriteOutput,
|
|
138
145
|
GlobOutput,
|
|
139
146
|
GrepOutput,
|
|
140
|
-
GrepContentOutput,
|
|
141
|
-
GrepFilesOutput,
|
|
142
|
-
GrepCountOutput,
|
|
143
147
|
WebFetchOutput,
|
|
144
148
|
WebSearchOutput,
|
|
145
|
-
WriteOutput,
|
|
146
149
|
NotebookEditOutput,
|
|
147
150
|
TodoWriteOutput,
|
|
148
|
-
|
|
149
|
-
KillShellOutput,
|
|
151
|
+
TaskStopOutput,
|
|
150
152
|
ListMcpResourcesOutput,
|
|
151
153
|
ReadMcpResourceOutput,
|
|
152
154
|
ToolOutput,
|