@straiffi/archon 1.2.8 → 1.2.10

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 (48) hide show
  1. package/dist/client/assets/{TestsDialog-XEE4TEv4.js → TestsDialog-BPHhOIWL.js} +1 -1
  2. package/dist/client/assets/{badge-RYXMXLW8.js → badge-DtLPY3ow.js} +12 -12
  3. package/dist/client/assets/index-FvaV49M1.js +149 -0
  4. package/dist/client/assets/index-Qf62KDwt.css +2 -0
  5. package/dist/client/index.html +3 -3
  6. package/dist/server/db.js +33 -0
  7. package/dist/server/db.js.map +1 -1
  8. package/dist/server/index.js +120 -3
  9. package/dist/server/index.js.map +1 -1
  10. package/dist/server/lib/agent.js +2 -2
  11. package/dist/server/lib/agent.js.map +1 -1
  12. package/dist/server/lib/attachments.js +202 -0
  13. package/dist/server/lib/attachments.js.map +1 -0
  14. package/dist/server/lib/buildFlow.js +1 -1
  15. package/dist/server/lib/buildFlow.js.map +1 -1
  16. package/dist/server/lib/chatMessages.js +4 -1
  17. package/dist/server/lib/chatMessages.js.map +1 -1
  18. package/dist/server/lib/chatOperations.js +33 -11
  19. package/dist/server/lib/chatOperations.js.map +1 -1
  20. package/dist/server/lib/chatTargets.js +11 -0
  21. package/dist/server/lib/chatTargets.js.map +1 -1
  22. package/dist/server/lib/chats.js +21 -4
  23. package/dist/server/lib/chats.js.map +1 -1
  24. package/dist/server/lib/runtimePaths.js +7 -0
  25. package/dist/server/lib/runtimePaths.js.map +1 -1
  26. package/dist/server/lib/thinkingState.js +67 -0
  27. package/dist/server/lib/thinkingState.js.map +1 -0
  28. package/dist/server/lib/ticketUndo.js +2 -2
  29. package/dist/server/lib/ticketUndo.js.map +1 -1
  30. package/dist/server/lib/ticketWorkflowOperations.js +1 -1
  31. package/dist/server/lib/ticketWorkflowOperations.js.map +1 -1
  32. package/dist/server/lib/tickets.js +6 -0
  33. package/dist/server/lib/tickets.js.map +1 -1
  34. package/dist/server/workers/build.js +6 -1
  35. package/dist/server/workers/build.js.map +1 -1
  36. package/dist/server/workers/chat.js +55 -9
  37. package/dist/server/workers/chat.js.map +1 -1
  38. package/dist/server/workers/followUp.js +6 -1
  39. package/dist/server/workers/followUp.js.map +1 -1
  40. package/dist/server/workers/plan.js +7 -2
  41. package/dist/server/workers/plan.js.map +1 -1
  42. package/dist/server/workers/planFollowUp.js +7 -2
  43. package/dist/server/workers/planFollowUp.js.map +1 -1
  44. package/dist/server/workers/review.js +5 -0
  45. package/dist/server/workers/review.js.map +1 -1
  46. package/package.json +5 -2
  47. package/dist/client/assets/index-BAYGY5L6.js +0 -149
  48. package/dist/client/assets/index-DWMCGEsT.css +0 -2
@@ -1,4 +1,5 @@
1
1
  import express from 'express';
2
+ import multer from 'multer';
2
3
  import { existsSync } from 'fs';
3
4
  import { createServer } from 'http';
4
5
  import { fileURLToPath } from 'url';
@@ -8,6 +9,7 @@ import cors from 'cors';
8
9
  import { adjectives, animals, uniqueNamesGenerator } from 'unique-names-generator';
9
10
  import db from './db.js';
10
11
  import { deleteChatSession, getChatSession, listChatSessions, updateChatSessionMode } from './lib/chats.js';
12
+ import { createChatAttachment, deleteChatAttachment, getChatAttachmentById, getChatAttachmentForSession, getChatAttachmentLinkedMessageId, MAX_CHAT_ATTACHMENTS_PER_TURN, MAX_CHAT_ATTACHMENT_SIZE_BYTES, readChatAttachmentContent, validateImageAttachment, } from './lib/attachments.js';
11
13
  import { createProjectChatSession, resolveChatMode, resolveChatTool, stopProjectChatSessionResponse, submitProjectChatSessionMessage, } from './lib/chatOperations.js';
12
14
  import { resolveChatSessionTarget } from './lib/chatTargets.js';
13
15
  import { createChatMessage, getChatMessage, updateChatMessageContent } from './lib/chatMessages.js';
@@ -275,6 +277,24 @@ const asTrimmedString = (value) => {
275
277
  return typeof value === 'string' ? value.trim() : '';
276
278
  };
277
279
  const COMMIT_MESSAGE_DIFF_CHAR_LIMIT = 12_000;
280
+ const chatAttachmentUpload = multer({
281
+ storage: multer.memoryStorage(),
282
+ limits: {
283
+ files: MAX_CHAT_ATTACHMENTS_PER_TURN,
284
+ fileSize: MAX_CHAT_ATTACHMENT_SIZE_BYTES,
285
+ },
286
+ });
287
+ const getChatAttachmentUploadErrorMessage = (error) => {
288
+ if (error instanceof multer.MulterError) {
289
+ if (error.code === 'LIMIT_FILE_COUNT') {
290
+ return 'You can attach up to 4 images per turn.';
291
+ }
292
+ if (error.code === 'LIMIT_FILE_SIZE') {
293
+ return 'Images must be 10 MB or smaller.';
294
+ }
295
+ }
296
+ return 'Unable to upload the selected images right now.';
297
+ };
278
298
  const normalizeGeneratedCommitMessage = (value) => {
279
299
  const firstLine = value
280
300
  .replace(/\r/g, '\n')
@@ -871,12 +891,12 @@ const listBundleConversationTickets = (projectId, bundleId) => {
871
891
  };
872
892
  const moveBundleTicketToReview = (ticket) => {
873
893
  const laneOrder = assignTicketLaneOrder({ ...ticket, state: 'review' });
874
- db.prepare('UPDATE tickets SET state = ?, tool = ?, agent_status = NULL, agent_log = NULL, streaming_response = NULL, lane_order = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ?').run('review', resolveTicketTool(ticket, config.tool ?? 'opencode'), laneOrder ?? null, ticket.id);
894
+ db.prepare('UPDATE tickets SET state = ?, tool = ?, agent_status = NULL, streaming_response = NULL, lane_order = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ?').run('review', resolveTicketTool(ticket, config.tool ?? 'opencode'), laneOrder ?? null, ticket.id);
875
895
  return getTicket(ticket.id);
876
896
  };
877
897
  const restoreBundleReviewTicketToBuild = (ticket) => {
878
898
  const laneOrder = assignTicketLaneOrder({ ...ticket, state: 'build' });
879
- db.prepare('UPDATE tickets SET state = ?, agent_status = ?, agent_log = NULL, streaming_response = NULL, lane_order = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ?').run('build', 'done', laneOrder ?? null, ticket.id);
899
+ db.prepare('UPDATE tickets SET state = ?, agent_status = ?, streaming_response = NULL, lane_order = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ?').run('build', 'done', laneOrder ?? null, ticket.id);
880
900
  return getTicket(ticket.id);
881
901
  };
882
902
  const resolveBundleLatestSessionTicket = (tickets) => {
@@ -2164,6 +2184,103 @@ app.post('/projects/:id/chat-sessions/:sessionId/messages', (req, res) => {
2164
2184
  }
2165
2185
  return res.status(result.status).json(result.value);
2166
2186
  });
2187
+ app.post('/projects/:id/chat-sessions/:sessionId/attachments', (req, res) => {
2188
+ const project = getProjectById(req.params.id);
2189
+ if (!project) {
2190
+ return res.status(404).json({ error: 'Not found' });
2191
+ }
2192
+ const chatSession = getProjectChatSession(project.id, req.params.sessionId);
2193
+ if (!chatSession) {
2194
+ return res.status(404).json({ error: 'Not found' });
2195
+ }
2196
+ if (chatSession.agent_status === 'running') {
2197
+ return res.status(409).json({ error: 'Wait for the current chat response to finish before uploading images.' });
2198
+ }
2199
+ return chatAttachmentUpload.array('files', MAX_CHAT_ATTACHMENTS_PER_TURN)(req, res, error => {
2200
+ if (error) {
2201
+ return res.status(400).json({ error: getChatAttachmentUploadErrorMessage(error) });
2202
+ }
2203
+ const files = (req.files ?? []);
2204
+ if (files.length === 0) {
2205
+ return res.status(400).json({ error: 'At least one image is required.' });
2206
+ }
2207
+ for (const file of files) {
2208
+ const validationError = validateImageAttachment({
2209
+ mimeType: file.mimetype,
2210
+ originalName: file.originalname,
2211
+ });
2212
+ if (validationError) {
2213
+ return res.status(400).json({ error: validationError });
2214
+ }
2215
+ }
2216
+ const createdAttachmentIds = [];
2217
+ try {
2218
+ const attachments = files.map(file => {
2219
+ const attachment = createChatAttachment({
2220
+ chatSessionId: chatSession.id,
2221
+ originalName: file.originalname,
2222
+ mimeType: file.mimetype,
2223
+ buffer: file.buffer,
2224
+ projectId: project.id,
2225
+ });
2226
+ createdAttachmentIds.push(attachment.id);
2227
+ return attachment;
2228
+ });
2229
+ return res.status(201).json({ attachments });
2230
+ }
2231
+ catch (uploadError) {
2232
+ for (const attachmentId of createdAttachmentIds) {
2233
+ const attachment = getChatAttachmentById(attachmentId);
2234
+ if (!attachment) {
2235
+ continue;
2236
+ }
2237
+ deleteChatAttachment(attachment);
2238
+ }
2239
+ return res.status(500).json({ error: uploadError instanceof Error ? uploadError.message : 'Unable to upload the selected images right now.' });
2240
+ }
2241
+ });
2242
+ });
2243
+ app.delete('/projects/:id/chat-sessions/:sessionId/attachments/:attachmentId', (req, res) => {
2244
+ const project = getProjectById(req.params.id);
2245
+ if (!project) {
2246
+ return res.status(404).json({ error: 'Not found' });
2247
+ }
2248
+ const chatSession = getProjectChatSession(project.id, req.params.sessionId);
2249
+ if (!chatSession) {
2250
+ return res.status(404).json({ error: 'Not found' });
2251
+ }
2252
+ const attachment = getChatAttachmentForSession(req.params.attachmentId, chatSession.id);
2253
+ if (!attachment) {
2254
+ return res.status(404).json({ error: 'Attachment not found' });
2255
+ }
2256
+ if (getChatAttachmentLinkedMessageId(attachment.id)) {
2257
+ return res.status(409).json({ error: 'Sent message attachments cannot be removed.' });
2258
+ }
2259
+ deleteChatAttachment(attachment);
2260
+ return res.json({ ok: true });
2261
+ });
2262
+ app.get('/projects/:id/chat-sessions/:sessionId/attachments/:attachmentId/content', (req, res) => {
2263
+ const project = getProjectById(req.params.id);
2264
+ if (!project) {
2265
+ return res.status(404).json({ error: 'Not found' });
2266
+ }
2267
+ const chatSession = getProjectChatSession(project.id, req.params.sessionId);
2268
+ if (!chatSession) {
2269
+ return res.status(404).json({ error: 'Not found' });
2270
+ }
2271
+ const attachment = getChatAttachmentForSession(req.params.attachmentId, chatSession.id);
2272
+ if (!attachment) {
2273
+ return res.status(404).json({ error: 'Attachment not found' });
2274
+ }
2275
+ try {
2276
+ const content = readChatAttachmentContent(attachment);
2277
+ res.type(attachment.mime_type);
2278
+ return res.send(content);
2279
+ }
2280
+ catch {
2281
+ return res.status(404).json({ error: 'Attachment content not found' });
2282
+ }
2283
+ });
2167
2284
  app.post('/projects/:id/chat-sessions/:sessionId/stop', async (req, res) => {
2168
2285
  const result = await stopProjectChatSessionResponse({
2169
2286
  projectId: req.params.id,
@@ -4640,7 +4757,7 @@ app.post('/tickets/:id/retry', (req, res) => {
4640
4757
  if (ticket.agent_status !== 'error') {
4641
4758
  return res.status(400).json({ error: 'Ticket is not in error state' });
4642
4759
  }
4643
- db.prepare('UPDATE tickets SET agent_status = NULL, agent_log = NULL, streaming_response = NULL, build_completed_at = NULL, auto_park_dismissed_at = NULL, updated_at = CURRENT_TIMESTAMP WHERE id = ?').run(req.params.id);
4760
+ db.prepare('UPDATE tickets SET agent_status = NULL, agent_log = NULL, agent_message_id = NULL, previous_agent_log = NULL, previous_agent_message_id = NULL, streaming_response = NULL, build_completed_at = NULL, auto_park_dismissed_at = NULL, updated_at = CURRENT_TIMESTAMP WHERE id = ?').run(req.params.id);
4644
4761
  const updated = getTicket(req.params.id);
4645
4762
  io.emit('ticket:updated', updated);
4646
4763
  res.json(updated);