@kirosnn/mosaic 0.71.0 → 0.74.0

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 (79) hide show
  1. package/README.md +1 -5
  2. package/package.json +4 -2
  3. package/src/agent/Agent.ts +353 -131
  4. package/src/agent/context.ts +4 -4
  5. package/src/agent/prompts/systemPrompt.ts +15 -6
  6. package/src/agent/prompts/toolsPrompt.ts +136 -10
  7. package/src/agent/provider/anthropic.ts +100 -100
  8. package/src/agent/provider/google.ts +102 -102
  9. package/src/agent/provider/mistral.ts +95 -95
  10. package/src/agent/provider/ollama.ts +77 -60
  11. package/src/agent/provider/openai.ts +42 -38
  12. package/src/agent/provider/rateLimit.ts +178 -0
  13. package/src/agent/provider/xai.ts +99 -99
  14. package/src/agent/tools/definitions.ts +19 -9
  15. package/src/agent/tools/executor.ts +95 -85
  16. package/src/agent/tools/exploreExecutor.ts +8 -10
  17. package/src/agent/tools/grep.ts +30 -29
  18. package/src/agent/tools/question.ts +7 -1
  19. package/src/agent/types.ts +9 -8
  20. package/src/components/App.tsx +45 -45
  21. package/src/components/CustomInput.tsx +214 -36
  22. package/src/components/Main.tsx +552 -339
  23. package/src/components/Setup.tsx +1 -1
  24. package/src/components/Welcome.tsx +1 -1
  25. package/src/components/main/ApprovalPanel.tsx +4 -3
  26. package/src/components/main/ChatPage.tsx +858 -675
  27. package/src/components/main/HomePage.tsx +53 -38
  28. package/src/components/main/QuestionPanel.tsx +52 -7
  29. package/src/components/main/ThinkingIndicator.tsx +2 -1
  30. package/src/index.tsx +50 -20
  31. package/src/mcp/approvalPolicy.ts +156 -0
  32. package/src/mcp/cli/add.ts +185 -0
  33. package/src/mcp/cli/doctor.ts +74 -0
  34. package/src/mcp/cli/index.ts +85 -0
  35. package/src/mcp/cli/list.ts +50 -0
  36. package/src/mcp/cli/logs.ts +24 -0
  37. package/src/mcp/cli/manage.ts +99 -0
  38. package/src/mcp/cli/show.ts +53 -0
  39. package/src/mcp/cli/tools.ts +77 -0
  40. package/src/mcp/config.ts +234 -0
  41. package/src/mcp/index.ts +80 -0
  42. package/src/mcp/processManager.ts +304 -0
  43. package/src/mcp/rateLimiter.ts +50 -0
  44. package/src/mcp/registry.ts +151 -0
  45. package/src/mcp/schemaConverter.ts +100 -0
  46. package/src/mcp/servers/navigation/browser.ts +151 -0
  47. package/src/mcp/servers/navigation/index.ts +23 -0
  48. package/src/mcp/servers/navigation/tools.ts +263 -0
  49. package/src/mcp/servers/navigation/types.ts +17 -0
  50. package/src/mcp/servers/navigation/utils.ts +20 -0
  51. package/src/mcp/toolCatalog.ts +182 -0
  52. package/src/mcp/types.ts +116 -0
  53. package/src/utils/approvalBridge.ts +17 -5
  54. package/src/utils/commands/compact.ts +30 -0
  55. package/src/utils/commands/echo.ts +1 -1
  56. package/src/utils/commands/index.ts +4 -6
  57. package/src/utils/commands/new.ts +15 -0
  58. package/src/utils/commands/types.ts +3 -0
  59. package/src/utils/config.ts +3 -1
  60. package/src/utils/diffRendering.tsx +1 -3
  61. package/src/utils/exploreBridge.ts +10 -0
  62. package/src/utils/markdown.tsx +220 -122
  63. package/src/utils/models.ts +31 -9
  64. package/src/utils/questionBridge.ts +36 -1
  65. package/src/utils/tokenEstimator.ts +32 -0
  66. package/src/utils/toolFormatting.ts +317 -7
  67. package/src/web/app.tsx +72 -72
  68. package/src/web/components/HomePage.tsx +7 -7
  69. package/src/web/components/MessageItem.tsx +66 -35
  70. package/src/web/components/QuestionPanel.tsx +72 -12
  71. package/src/web/components/Sidebar.tsx +0 -2
  72. package/src/web/components/ThinkingIndicator.tsx +1 -0
  73. package/src/web/server.tsx +767 -683
  74. package/src/utils/commands/redo.ts +0 -74
  75. package/src/utils/commands/sessions.ts +0 -129
  76. package/src/utils/commands/undo.ts +0 -75
  77. package/src/utils/undoRedo.ts +0 -429
  78. package/src/utils/undoRedoBridge.ts +0 -45
  79. package/src/utils/undoRedoDb.ts +0 -338
@@ -0,0 +1,116 @@
1
+ export interface McpTransportConfig {
2
+ type: 'stdio';
3
+ }
4
+
5
+ export interface McpServerConfig {
6
+ id: string;
7
+ name: string;
8
+ enabled: boolean;
9
+ native?: boolean;
10
+ transport: McpTransportConfig;
11
+ command: string;
12
+ args: string[];
13
+ cwd?: string;
14
+ env?: Record<string, string>;
15
+ autostart: 'startup' | 'on-demand' | 'never';
16
+ timeouts: {
17
+ initialize: number;
18
+ call: number;
19
+ };
20
+ limits: {
21
+ maxCallsPerMinute: number;
22
+ maxPayloadBytes: number;
23
+ };
24
+ logs: {
25
+ persist: boolean;
26
+ path?: string;
27
+ bufferSize: number;
28
+ };
29
+ tools: {
30
+ allow?: string[];
31
+ deny?: string[];
32
+ };
33
+ approval: McpApprovalMode;
34
+ toolApproval?: Record<string, McpApprovalMode>;
35
+ }
36
+
37
+ export type McpApprovalMode = 'always' | 'once-per-tool' | 'once-per-server' | 'never';
38
+
39
+ export type McpServerStatus = 'stopped' | 'starting' | 'running' | 'error';
40
+
41
+ export interface McpServerState {
42
+ status: McpServerStatus;
43
+ pid?: number;
44
+ initLatencyMs?: number;
45
+ toolCount: number;
46
+ lastError?: string;
47
+ lastCallAt?: number;
48
+ }
49
+
50
+ export interface McpToolInfo {
51
+ serverId: string;
52
+ name: string;
53
+ description: string;
54
+ inputSchema: Record<string, unknown>;
55
+ canonicalId: string;
56
+ safeId: string;
57
+ }
58
+
59
+ export type McpRiskHint = 'read' | 'write' | 'execute' | 'network' | 'unknown';
60
+
61
+ export type McpApprovalScope = 'toolArgs' | 'tool' | 'server';
62
+
63
+ export interface McpApprovalCacheEntry {
64
+ scope: McpApprovalScope;
65
+ key: string;
66
+ expiresAt: number;
67
+ }
68
+
69
+ export interface McpGlobalConfig {
70
+ servers: McpServerConfig[];
71
+ }
72
+
73
+ export function toCanonicalId(serverId: string, toolName: string): string {
74
+ return `mcp:${serverId}:${toolName}`;
75
+ }
76
+
77
+ export function toSafeId(serverId: string, toolName: string): string {
78
+ return `mcp__${serverId}__${toolName}`;
79
+ }
80
+
81
+ export function parseSafeId(safeId: string): { serverId: string; toolName: string } | null {
82
+ if (!safeId.startsWith('mcp__')) return null;
83
+ const parts = safeId.slice(5).split('__');
84
+ if (parts.length < 2) return null;
85
+ const toolName = parts.pop()!;
86
+ const serverId = parts.join('__');
87
+ return { serverId, toolName };
88
+ }
89
+
90
+ export function parseCanonicalId(canonicalId: string): { serverId: string; toolName: string } | null {
91
+ if (!canonicalId.startsWith('mcp:')) return null;
92
+ const parts = canonicalId.slice(4).split(':');
93
+ if (parts.length < 2) return null;
94
+ const toolName = parts.pop()!;
95
+ const serverId = parts.join(':');
96
+ return { serverId, toolName };
97
+ }
98
+
99
+ export const NATIVE_SERVER_IDS = new Set(['navigation']);
100
+
101
+ export function isNativeMcpServer(serverId: string): boolean {
102
+ return NATIVE_SERVER_IDS.has(serverId);
103
+ }
104
+
105
+ export function isNativeMcpTool(safeId: string): boolean {
106
+ const parsed = parseSafeId(safeId);
107
+ if (!parsed) return false;
108
+ return NATIVE_SERVER_IDS.has(parsed.serverId);
109
+ }
110
+
111
+ export function getNativeMcpToolName(safeId: string): string | null {
112
+ const parsed = parseSafeId(safeId);
113
+ if (!parsed) return null;
114
+ if (!NATIVE_SERVER_IDS.has(parsed.serverId)) return null;
115
+ return parsed.toolName;
116
+ }
@@ -1,12 +1,19 @@
1
1
  export interface ApprovalRequest {
2
2
  id: string;
3
- toolName: 'write' | 'edit' | 'bash';
3
+ toolName: string;
4
4
  preview: {
5
5
  title: string;
6
6
  content: string;
7
7
  details?: string[];
8
8
  };
9
9
  args: Record<string, unknown>;
10
+ mcpMeta?: {
11
+ serverId: string;
12
+ serverName: string;
13
+ canonicalId: string;
14
+ riskHint: string;
15
+ payloadSize: number;
16
+ };
10
17
  }
11
18
 
12
19
  export interface ApprovalResponse {
@@ -16,7 +23,7 @@ export interface ApprovalResponse {
16
23
  }
17
24
 
18
25
  export interface ApprovalAccepted {
19
- toolName: 'write' | 'edit' | 'bash';
26
+ toolName: string;
20
27
  args: Record<string, unknown>;
21
28
  }
22
29
 
@@ -59,7 +66,7 @@ export function subscribeApprovalAccepted(listener: ApprovalAcceptedListener): (
59
66
  };
60
67
  }
61
68
 
62
- function notifyApprovalAccepted(toolName: 'write' | 'edit' | 'bash', args: Record<string, unknown>): void {
69
+ function notifyApprovalAccepted(toolName: string, args: Record<string, unknown>): void {
63
70
  for (const listener of acceptedListeners) {
64
71
  listener({ toolName, args });
65
72
  }
@@ -70,15 +77,20 @@ export function getCurrentApproval(): ApprovalRequest | null {
70
77
  }
71
78
 
72
79
  export async function requestApproval(
73
- toolName: 'write' | 'edit' | 'bash',
80
+ toolName: string,
74
81
  args: Record<string, unknown>,
75
82
  preview: { title: string; content: string; details?: string[] }
76
83
  ): Promise<{ approved: boolean; customResponse?: string }> {
84
+ const mcpMeta = (args as any).__mcpMeta;
85
+ const cleanArgs = { ...args };
86
+ delete (cleanArgs as any).__mcpMeta;
87
+
77
88
  const request: ApprovalRequest = {
78
89
  id: createId(),
79
90
  toolName,
80
91
  preview,
81
- args,
92
+ args: cleanArgs,
93
+ ...(mcpMeta && { mcpMeta }),
82
94
  };
83
95
 
84
96
  const response = await new Promise<ApprovalResponse>((resolve, reject) => {
@@ -0,0 +1,30 @@
1
+ import type { Command, CommandResult } from './types';
2
+
3
+ export const compactCommand: Command = {
4
+ name: 'compact',
5
+ description: 'Compact the current conversation context',
6
+ usage: '/compact [maxTokens]',
7
+ execute: (args: string[]): CommandResult => {
8
+ let maxTokens: number | undefined;
9
+ if (args[0]) {
10
+ const parsed = Number(args[0]);
11
+ if (!Number.isFinite(parsed) || parsed <= 0) {
12
+ return {
13
+ success: false,
14
+ content: 'Invalid maxTokens. Usage: /compact [maxTokens]',
15
+ shouldAddToHistory: false
16
+ };
17
+ }
18
+ maxTokens = Math.floor(parsed);
19
+ }
20
+
21
+ return {
22
+ success: true,
23
+ content: 'Conversation compacted.',
24
+ shouldAddToHistory: false,
25
+ shouldCompactMessages: true,
26
+ compactMaxTokens: maxTokens,
27
+ shouldClearMessages: true
28
+ };
29
+ }
30
+ };
@@ -5,7 +5,7 @@ export const echoCommand: Command = {
5
5
  description: 'Echo the provided text back to the user',
6
6
  usage: '/echo <text>',
7
7
  aliases: ['e'],
8
- execute: (args: string[], fullCommand: string): { success: boolean; content: string } => {
8
+ execute: (args: string[], _fullCommand: string): { success: boolean; content: string } => {
9
9
  if (args.length === 0) {
10
10
  return {
11
11
  success: false,
@@ -3,12 +3,11 @@ import { commandRegistry } from './registry';
3
3
  import { echoCommand } from './echo';
4
4
  import { helpCommand } from './help';
5
5
  import { initCommand } from './init';
6
- import { undoCommand } from './undo';
7
- import { redoCommand } from './redo';
8
- import { sessionsCommand } from './sessions';
9
6
  import { webCommand } from './web';
10
7
  import { imageCommand } from './image';
11
8
  import { approvalsCommand } from './approvals';
9
+ import { newCommand } from './new';
10
+ import { compactCommand } from './compact';
12
11
 
13
12
  export { commandRegistry } from './registry';
14
13
  export type { Command, CommandResult, CommandRegistry } from './types';
@@ -63,10 +62,9 @@ export function initializeCommands(): void {
63
62
  commandRegistry.register(echoCommand);
64
63
  commandRegistry.register(helpCommand);
65
64
  commandRegistry.register(initCommand);
66
- commandRegistry.register(undoCommand);
67
- commandRegistry.register(redoCommand);
68
- commandRegistry.register(sessionsCommand);
69
65
  commandRegistry.register(webCommand);
70
66
  commandRegistry.register(imageCommand);
71
67
  commandRegistry.register(approvalsCommand);
68
+ commandRegistry.register(newCommand);
69
+ commandRegistry.register(compactCommand);
72
70
  }
@@ -0,0 +1,15 @@
1
+ import type { Command, CommandResult } from './types';
2
+
3
+ export const newCommand: Command = {
4
+ name: 'new',
5
+ description: 'Start a new chat',
6
+ usage: '/new',
7
+ aliases: ['clear'],
8
+ execute: (_args: string[], _fullCommand: string): CommandResult => {
9
+ return {
10
+ success: true,
11
+ content: 'Starting a new chat...',
12
+ shouldClearMessages: true
13
+ };
14
+ }
15
+ };
@@ -2,6 +2,9 @@ export interface CommandResult {
2
2
  success: boolean;
3
3
  content: string;
4
4
  shouldAddToHistory?: boolean;
5
+ shouldClearMessages?: boolean;
6
+ shouldCompactMessages?: boolean;
7
+ compactMaxTokens?: number;
5
8
  }
6
9
 
7
10
  export interface Command {
@@ -38,6 +38,8 @@ export interface MosaicConfig {
38
38
  model?: string;
39
39
  apiKey?: string;
40
40
  systemPrompt?: string;
41
+ maxSteps?: number;
42
+ maxContextTokens?: number;
41
43
  customProviders?: CustomProvider[];
42
44
  customModels?: { [providerId: string]: AIModel[] };
43
45
  requireApprovals?: boolean;
@@ -354,4 +356,4 @@ export function clearRecentProjects(): void {
354
356
  const config = readConfig();
355
357
  config.recentProjects = [];
356
358
  writeConfig(config);
357
- }
359
+ }
@@ -34,8 +34,6 @@ export function renderDiffLine(line: string, key: string) {
34
34
 
35
35
  export function renderInlineDiffLine(content: string) {
36
36
  const parsed = parseDiffLine(content);
37
- const colors = getDiffLineColors(parsed);
38
-
39
37
  if (parsed.isDiffLine) {
40
38
  return (
41
39
  <>
@@ -56,6 +54,6 @@ export function renderInlineDiffLine(content: string) {
56
54
  return null;
57
55
  }
58
56
 
59
- export function getDiffLineBackground(content: string): string | null {
57
+ export function getDiffLineBackground(_content: string): string | null {
60
58
  return null;
61
59
  }
@@ -15,6 +15,7 @@ interface ExploreBridgeGlobal {
15
15
  toolCallback: ExploreToolCallback | null;
16
16
  totalExploreTokens: number;
17
17
  subscribers: Set<ExploreToolSubscriber>;
18
+ parentContext: string;
18
19
  }
19
20
 
20
21
  const globalKey = '__mosaic_explore_bridge__';
@@ -26,6 +27,7 @@ if (!g[globalKey]) {
26
27
  toolCallback: null,
27
28
  totalExploreTokens: 0,
28
29
  subscribers: new Set<ExploreToolSubscriber>(),
30
+ parentContext: '',
29
31
  };
30
32
  }
31
33
 
@@ -85,3 +87,11 @@ export function subscribeExploreTool(callback: ExploreToolSubscriber): () => voi
85
87
  export function getExploreTokens(): number {
86
88
  return state.totalExploreTokens;
87
89
  }
90
+
91
+ export function setExploreContext(context: string): void {
92
+ state.parentContext = context;
93
+ }
94
+
95
+ export function getExploreContext(): string {
96
+ return state.parentContext;
97
+ }