vzcode 2.13.0 → 2.14.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.
package/dist/index.html CHANGED
@@ -20,7 +20,7 @@
20
20
  href="https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600;700&display=swap"
21
21
  rel="stylesheet"
22
22
  />
23
- <script type="module" crossorigin src="/assets/index-858eZSOF.js"></script>
23
+ <script type="module" crossorigin src="/assets/index-AX9wEDr7.js"></script>
24
24
  <link rel="stylesheet" crossorigin href="/assets/index-DLm5FQ0E.css">
25
25
  </head>
26
26
  <body>
@@ -10,8 +10,8 @@ const delayStart = false;
10
10
  * Performs AI chat without editing - just generates a response
11
11
  */
12
12
  export const performAIChat = async ({ prompt, shareDBDoc, llmFunction, }) => {
13
- const preparedFiles = prepareFilesForPrompt(shareDBDoc.data.files);
14
- const filesContext = formatMarkdownFiles(preparedFiles);
13
+ const { files } = prepareFilesForPrompt(shareDBDoc.data.files);
14
+ const filesContext = formatMarkdownFiles(files);
15
15
  // 2. Assemble the final prompt for Q&A mode
16
16
  const fullPrompt = assembleFullPrompt({
17
17
  filesContext,
@@ -35,8 +35,8 @@ export const performAIChat = async ({ prompt, shareDBDoc, llmFunction, }) => {
35
35
  export const performAIEditing = async ({ prompt, shareDBDoc, llmFunction, runCode, }) => {
36
36
  // 1. Capture the current state of files before editing
37
37
  const beforeFiles = createFilesSnapshot(shareDBDoc.data.files);
38
- const preparedFiles = prepareFilesForPrompt(shareDBDoc.data.files);
39
- const filesContext = formatMarkdownFiles(preparedFiles);
38
+ const { files } = prepareFilesForPrompt(shareDBDoc.data.files);
39
+ const filesContext = formatMarkdownFiles(files);
40
40
  // 2. Assemble the final prompt
41
41
  const fullPrompt = assembleFullPrompt({
42
42
  filesContext,
@@ -5,8 +5,9 @@ import { performAIEditing, performAIChat, } from './aiEditing.js';
5
5
  import { handleError, handleBackgroundError, } from './errorHandling.js';
6
6
  import { createRunCodeFunction } from '../../runCode.js';
7
7
  import { createSubmitOperation } from '../../submitOperation.js';
8
+ import { getGenerationMetadata } from 'editcodewithai';
8
9
  const DEBUG = false;
9
- export const handleAIChatMessage = ({ shareDBDoc, createAIEditLocalPresence, onCreditDeduction, getCurrentCommitId, model, aiRequestOptions, }) => async (req, res) => {
10
+ export const handleAIChatMessage = ({ shareDBDoc, onCreditDeduction, getCurrentCommitId, model, aiRequestOptions, }) => async (req, res) => {
10
11
  const { content, chatId, mode = 'edit' } = req.body;
11
12
  if (DEBUG) {
12
13
  console.log('[handleAIChatMessage] content:', content, 'chatId:', chatId, 'shareDBDoc:', shareDBDoc);
@@ -31,7 +32,6 @@ export const handleAIChatMessage = ({ shareDBDoc, createAIEditLocalPresence, onC
31
32
  chatId,
32
33
  content,
33
34
  mode,
34
- createAIEditLocalPresence,
35
35
  getCurrentCommitId,
36
36
  model,
37
37
  aiRequestOptions,
@@ -49,7 +49,7 @@ export const handleAIChatMessage = ({ shareDBDoc, createAIEditLocalPresence, onC
49
49
  /**
50
50
  * Processes the AI request asynchronously in the background
51
51
  */
52
- const processAIRequestAsync = async ({ shareDBDoc, chatId, content, mode, createAIEditLocalPresence, getCurrentCommitId, model, aiRequestOptions, onCreditDeduction, }) => {
52
+ const processAIRequestAsync = async ({ shareDBDoc, chatId, content, mode, getCurrentCommitId, model, aiRequestOptions, onCreditDeduction, }) => {
53
53
  try {
54
54
  // Capture the current commit ID before making changes (for VizHub integration)
55
55
  const beforeCommitId = getCurrentCommitId
@@ -58,7 +58,6 @@ const processAIRequestAsync = async ({ shareDBDoc, chatId, content, mode, create
58
58
  // Create LLM function for streaming
59
59
  const llmFunction = createLLMFunction({
60
60
  shareDBDoc,
61
- createAIEditLocalPresence,
62
61
  chatId,
63
62
  model,
64
63
  aiRequestOptions,
@@ -85,16 +84,12 @@ const processAIRequestAsync = async ({ shareDBDoc, chatId, content, mode, create
85
84
  addDiffToAIMessage(shareDBDoc, chatId, editResult.diffData, beforeCommitId);
86
85
  }
87
86
  // Handle credit deduction if callback is provided
88
- if (onCreditDeduction &&
89
- editResult.upstreamCostCents) {
87
+ if (onCreditDeduction && editResult.generationId) {
90
88
  try {
91
- await onCreditDeduction({
92
- upstreamCostCents: editResult
93
- .upstreamCostCents,
94
- provider: editResult.provider,
95
- inputTokens: editResult.inputTokens,
96
- outputTokens: editResult.outputTokens,
97
- });
89
+ await onCreditDeduction(await getGenerationMetadata({
90
+ apiKey: process.env.VZCODE_EDIT_WITH_AI_API_KEY,
91
+ generationId: editResult.generationId,
92
+ }));
98
93
  }
99
94
  catch (creditError) {
100
95
  console.error('Credit deduction error:', creditError);
@@ -1,9 +1,9 @@
1
- import { parseMarkdownFiles, StreamingMarkdownParser, } from 'llm-code-format';
2
- import OpenAI from 'openai';
3
1
  import fs from 'fs';
2
+ import OpenAI from 'openai';
3
+ import { parseMarkdownFiles, StreamingMarkdownParser, } from 'llm-code-format';
4
+ import { mergeFileChanges } from 'editcodewithai';
4
5
  import { generateRunId } from '@vizhub/viz-utils';
5
6
  import { updateAIStatus, createAIMessage, updateAIMessageContent, finalizeAIMessage, ensureFileExists, clearFileContent, appendLineToFile, updateFiles, updateAIScratchpad, } from './chatOperations.js';
6
- import { mergeFileChanges } from 'editcodewithai';
7
7
  import { diff } from '../../ot.js';
8
8
  const DEBUG = false;
9
9
  // Useful for testing/debugging the streaming behavior
@@ -32,15 +32,12 @@ const enableStreamingEditing = false;
32
32
  /**
33
33
  * Creates and configures the LLM function for streaming with reasoning tokens
34
34
  */
35
- export const createLLMFunction = ({ shareDBDoc, createAIEditLocalPresence, chatId,
35
+ export const createLLMFunction = ({ shareDBDoc, chatId,
36
36
  // Feature flag to enable/disable reasoning tokens.
37
37
  // When false, reasoning tokens are not requested from the API
38
38
  // and reasoning content is not processed in the streaming response.
39
39
  enableReasoningTokens = false, model, aiRequestOptions, }) => {
40
40
  return async (fullPrompt) => {
41
- const localPresence = enableStreamingEditing
42
- ? createAIEditLocalPresence()
43
- : null;
44
41
  // Create OpenRouter client for reasoning token support
45
42
  const openRouterClient = new OpenAI({
46
43
  apiKey: process.env.VZCODE_EDIT_WITH_AI_API_KEY,
@@ -123,33 +120,6 @@ enableReasoningTokens = false, model, aiRequestOptions, }) => {
123
120
  if (currentEditingFileId) {
124
121
  // Apply OT operation for this line immediately
125
122
  appendLineToFile(shareDBDoc, currentEditingFileId, line);
126
- // Update AI presence to show cursor at the end of the file
127
- const currentFile = shareDBDoc.data.files[currentEditingFileId];
128
- if (currentFile && currentFile.text) {
129
- const textLength = currentFile.text.length;
130
- const filePresence = {
131
- username: 'AI Editor',
132
- start: [
133
- 'files',
134
- currentEditingFileId,
135
- 'text',
136
- textLength,
137
- ],
138
- end: [
139
- 'files',
140
- currentEditingFileId,
141
- 'text',
142
- textLength,
143
- ],
144
- };
145
- if (localPresence) {
146
- localPresence.submit(filePresence, (error) => {
147
- if (error) {
148
- console.warn('AI Editor line presence submission error:', error);
149
- }
150
- });
151
- }
152
- }
153
123
  }
154
124
  },
155
125
  onNonCodeLine: async (line) => {
@@ -238,17 +208,6 @@ enableReasoningTokens = false, model, aiRequestOptions, }) => {
238
208
  throttledUpdateAIMessageContent.flush();
239
209
  // Finalize the AI message by clearing temporary fields
240
210
  finalizeAIMessage(shareDBDoc, chatId);
241
- // Clear AI Editor presence when done
242
- DEBUG &&
243
- console.log('AI editing done, clearing AI Editor presence');
244
- if (localPresence) {
245
- localPresence.submit(null, (error) => {
246
- DEBUG && console.log('AI Editor presence cleared');
247
- if (error) {
248
- console.warn('AI Editor presence cleanup error:', error);
249
- }
250
- });
251
- }
252
211
  // If streaming editing is not enabled, we need to
253
212
  // apply all the edits at once
254
213
  if (!enableStreamingEditing) {
@@ -78,14 +78,6 @@ app.use(express.static(dir));
78
78
  // which is a representation of files on disk.
79
79
  const shareDBConnection = shareDBBackend.connect();
80
80
  const shareDBDoc = shareDBConnection.get('documents', '1');
81
- // Set up presence for AI editing following the same pattern as useShareDB.ts
82
- const docPresence = shareDBConnection.getDocPresence('documents', '1');
83
- // Create local presence for AI editing with a unique ID
84
- const generateAIEditId = () => {
85
- const timestamp = Date.now().toString(36);
86
- return `ai-edit-${timestamp}`;
87
- };
88
- const createAIEditLocalPresence = () => docPresence.create(generateAIEditId());
89
81
  shareDBDoc.create(initialDocument, json1Presence.type.uri);
90
82
  // Handle AI Assist requests.
91
83
  app.post('/ai-assist', bodyParser.json(), handleAIAssist(shareDBDoc));
@@ -94,7 +86,6 @@ app.post('/ai-copilot', bodyParser.json(), handleAICopilot());
94
86
  // Handle AI Chat Message requests.
95
87
  app.post('/ai-chat-message', bodyParser.json(), handleAIChatMessage({
96
88
  shareDBDoc,
97
- createAIEditLocalPresence,
98
89
  onCreditDeduction: undefined,
99
90
  }));
100
91
  // Livekit Token Generator
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vzcode",
3
- "version": "2.13.0",
3
+ "version": "2.14.0",
4
4
  "description": "Multiplayer code editor system",
5
5
  "main": "src/index.ts",
6
6
  "type": "module",
@@ -156,7 +156,7 @@
156
156
  "diff-match-patch": "^1.0.5",
157
157
  "diff2html": "^3.4.52",
158
158
  "dotenv": "^17.2.2",
159
- "editcodewithai": "^2.3.0",
159
+ "editcodewithai": "^2.4.0",
160
160
  "eslint-linter-browserify": "^9.34.0",
161
161
  "express": "^5.1.0",
162
162
  "ignore": "^7.0.5",
@@ -165,7 +165,7 @@
165
165
  "livekit-server-sdk": "^2.13.3",
166
166
  "llm-code-format": "^3.0.0",
167
167
  "lucide-react": "^0.542.0",
168
- "npm": "^11.5.2",
168
+ "npm": "^11.6.0",
169
169
  "open": "^10.2.0",
170
170
  "prettier-plugin-svelte": "^3.4.0",
171
171
  "react": "^18",
@@ -23,10 +23,10 @@ export const performAIChat = async ({
23
23
  shareDBDoc,
24
24
  llmFunction,
25
25
  }) => {
26
- const preparedFiles = prepareFilesForPrompt(
26
+ const { files } = prepareFilesForPrompt(
27
27
  shareDBDoc.data.files,
28
28
  );
29
- const filesContext = formatMarkdownFiles(preparedFiles);
29
+ const filesContext = formatMarkdownFiles(files);
30
30
 
31
31
  // 2. Assemble the final prompt for Q&A mode
32
32
  const fullPrompt = assembleFullPrompt({
@@ -65,10 +65,10 @@ export const performAIEditing = async ({
65
65
  shareDBDoc.data.files,
66
66
  );
67
67
 
68
- const preparedFiles = prepareFilesForPrompt(
68
+ const { files } = prepareFilesForPrompt(
69
69
  shareDBDoc.data.files,
70
70
  );
71
- const filesContext = formatMarkdownFiles(preparedFiles);
71
+ const filesContext = formatMarkdownFiles(files);
72
72
 
73
73
  // 2. Assemble the final prompt
74
74
  const fullPrompt = assembleFullPrompt({
@@ -19,20 +19,19 @@ import { createRunCodeFunction } from '../../runCode.js';
19
19
  import { ShareDBDoc } from '../../types.js';
20
20
  import { VizContent } from '@vizhub/viz-types';
21
21
  import { createSubmitOperation } from '../../submitOperation.js';
22
+ import { getGenerationMetadata } from 'editcodewithai';
22
23
 
23
24
  const DEBUG = false;
24
25
 
25
26
  export const handleAIChatMessage =
26
27
  ({
27
28
  shareDBDoc,
28
- createAIEditLocalPresence,
29
29
  onCreditDeduction,
30
30
  getCurrentCommitId,
31
31
  model,
32
32
  aiRequestOptions,
33
33
  }: {
34
34
  shareDBDoc: ShareDBDoc<VizContent>;
35
- createAIEditLocalPresence: () => any;
36
35
  onCreditDeduction?: any;
37
36
  getCurrentCommitId?: () => string | null;
38
37
  model?: string;
@@ -77,7 +76,6 @@ export const handleAIChatMessage =
77
76
  chatId,
78
77
  content,
79
78
  mode,
80
- createAIEditLocalPresence,
81
79
  getCurrentCommitId,
82
80
  model,
83
81
  aiRequestOptions,
@@ -103,7 +101,6 @@ const processAIRequestAsync = async ({
103
101
  chatId,
104
102
  content,
105
103
  mode,
106
- createAIEditLocalPresence,
107
104
  getCurrentCommitId,
108
105
  model,
109
106
  aiRequestOptions,
@@ -113,7 +110,6 @@ const processAIRequestAsync = async ({
113
110
  chatId: string;
114
111
  content: string;
115
112
  mode: string;
116
- createAIEditLocalPresence: () => any;
117
113
  getCurrentCommitId?: () => string | null;
118
114
  model?: string;
119
115
  aiRequestOptions?: any;
@@ -128,7 +124,6 @@ const processAIRequestAsync = async ({
128
124
  // Create LLM function for streaming
129
125
  const llmFunction = createLLMFunction({
130
126
  shareDBDoc,
131
- createAIEditLocalPresence,
132
127
  chatId,
133
128
  model,
134
129
  aiRequestOptions,
@@ -168,18 +163,14 @@ const processAIRequestAsync = async ({
168
163
  }
169
164
 
170
165
  // Handle credit deduction if callback is provided
171
- if (
172
- onCreditDeduction &&
173
- (editResult as any).upstreamCostCents
174
- ) {
166
+ if (onCreditDeduction && editResult.generationId) {
175
167
  try {
176
- await onCreditDeduction({
177
- upstreamCostCents: (editResult as any)
178
- .upstreamCostCents,
179
- provider: (editResult as any).provider,
180
- inputTokens: (editResult as any).inputTokens,
181
- outputTokens: (editResult as any).outputTokens,
182
- });
168
+ await onCreditDeduction(
169
+ await getGenerationMetadata({
170
+ apiKey: process.env.VZCODE_EDIT_WITH_AI_API_KEY,
171
+ generationId: editResult.generationId,
172
+ }),
173
+ );
183
174
  } catch (creditError) {
184
175
  console.error(
185
176
  'Credit deduction error:',
@@ -1,9 +1,11 @@
1
+ import fs from 'fs';
2
+ import OpenAI from 'openai';
1
3
  import {
2
4
  parseMarkdownFiles,
3
5
  StreamingMarkdownParser,
4
6
  } from 'llm-code-format';
5
- import OpenAI from 'openai';
6
- import fs from 'fs';
7
+ import { mergeFileChanges } from 'editcodewithai';
8
+ import { VizChatId, VizContent } from '@vizhub/viz-types';
7
9
  import { generateRunId } from '@vizhub/viz-utils';
8
10
  import {
9
11
  updateAIStatus,
@@ -16,9 +18,7 @@ import {
16
18
  updateFiles,
17
19
  updateAIScratchpad,
18
20
  } from './chatOperations.js';
19
- import { mergeFileChanges } from 'editcodewithai';
20
21
  import { diff } from '../../ot.js';
21
- import { VizChatId, VizContent } from '@vizhub/viz-types';
22
22
  import { ShareDBDoc } from '../../types.js';
23
23
 
24
24
  const DEBUG = false;
@@ -54,7 +54,6 @@ const enableStreamingEditing = false;
54
54
  */
55
55
  export const createLLMFunction = ({
56
56
  shareDBDoc,
57
- createAIEditLocalPresence,
58
57
  chatId,
59
58
  // Feature flag to enable/disable reasoning tokens.
60
59
  // When false, reasoning tokens are not requested from the API
@@ -64,17 +63,12 @@ export const createLLMFunction = ({
64
63
  aiRequestOptions,
65
64
  }: {
66
65
  shareDBDoc: ShareDBDoc<VizContent>;
67
- createAIEditLocalPresence: () => any;
68
66
  chatId: VizChatId;
69
67
  enableReasoningTokens?: boolean;
70
68
  model?: string;
71
69
  aiRequestOptions?: any;
72
70
  }) => {
73
71
  return async (fullPrompt: string) => {
74
- const localPresence = enableStreamingEditing
75
- ? createAIEditLocalPresence()
76
- : null;
77
-
78
72
  // Create OpenRouter client for reasoning token support
79
73
  const openRouterClient = new OpenAI({
80
74
  apiKey: process.env.VZCODE_EDIT_WITH_AI_API_KEY,
@@ -198,42 +192,6 @@ export const createLLMFunction = ({
198
192
  currentEditingFileId,
199
193
  line,
200
194
  );
201
-
202
- // Update AI presence to show cursor at the end of the file
203
- const currentFile =
204
- shareDBDoc.data.files[currentEditingFileId];
205
- if (currentFile && currentFile.text) {
206
- const textLength = currentFile.text.length;
207
- const filePresence = {
208
- username: 'AI Editor',
209
- start: [
210
- 'files',
211
- currentEditingFileId,
212
- 'text',
213
- textLength,
214
- ],
215
- end: [
216
- 'files',
217
- currentEditingFileId,
218
- 'text',
219
- textLength,
220
- ],
221
- };
222
-
223
- if (localPresence) {
224
- localPresence.submit(
225
- filePresence,
226
- (error) => {
227
- if (error) {
228
- console.warn(
229
- 'AI Editor line presence submission error:',
230
- error,
231
- );
232
- }
233
- },
234
- );
235
- }
236
- }
237
195
  }
238
196
  },
239
197
  onNonCodeLine: async (line: string) => {
@@ -348,23 +306,6 @@ export const createLLMFunction = ({
348
306
  // Finalize the AI message by clearing temporary fields
349
307
  finalizeAIMessage(shareDBDoc, chatId);
350
308
 
351
- // Clear AI Editor presence when done
352
- DEBUG &&
353
- console.log(
354
- 'AI editing done, clearing AI Editor presence',
355
- );
356
- if (localPresence) {
357
- localPresence.submit(null, (error) => {
358
- DEBUG && console.log('AI Editor presence cleared');
359
- if (error) {
360
- console.warn(
361
- 'AI Editor presence cleanup error:',
362
- error,
363
- );
364
- }
365
- });
366
- }
367
-
368
309
  // If streaming editing is not enabled, we need to
369
310
  // apply all the edits at once
370
311
  if (!enableStreamingEditing) {
@@ -14,7 +14,6 @@ import { computeInitialDocument } from './computeInitialDocument.js';
14
14
  import { handleAIAssist } from './handleAIAssist.js';
15
15
  import { handleAICopilot } from './handleAICopilot.js';
16
16
  import { handleAIChatMessage } from './handleAIChatMessage.js';
17
-
18
17
  import { isDirectory } from './isDirectory.js';
19
18
  import { createToken } from './livekit.js';
20
19
  import './setupEnv.js';
@@ -100,21 +99,6 @@ app.use(express.static(dir));
100
99
  const shareDBConnection = shareDBBackend.connect();
101
100
  const shareDBDoc = shareDBConnection.get('documents', '1');
102
101
 
103
- // Set up presence for AI editing following the same pattern as useShareDB.ts
104
- const docPresence = shareDBConnection.getDocPresence(
105
- 'documents',
106
- '1',
107
- );
108
-
109
- // Create local presence for AI editing with a unique ID
110
- const generateAIEditId = () => {
111
- const timestamp = Date.now().toString(36);
112
- return `ai-edit-${timestamp}`;
113
- };
114
-
115
- const createAIEditLocalPresence = () =>
116
- docPresence.create(generateAIEditId());
117
-
118
102
  shareDBDoc.create(initialDocument, json1Presence.type.uri);
119
103
 
120
104
  // Handle AI Assist requests.
@@ -137,7 +121,6 @@ app.post(
137
121
  bodyParser.json(),
138
122
  handleAIChatMessage({
139
123
  shareDBDoc,
140
- createAIEditLocalPresence,
141
124
  onCreditDeduction: undefined,
142
125
  }),
143
126
  );