@mindstudio-ai/remy 0.1.227 → 0.1.229

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/headless.js CHANGED
@@ -6362,7 +6362,9 @@ var SESSION_FILE = ".remy-session.json";
6362
6362
  var ARCHIVE_DIR = ".logs/sessions";
6363
6363
  var ROTATE_THRESHOLD_BYTES = 32 * 1024 * 1024;
6364
6364
  var RETAIN_TAIL_BYTES = 16 * 1024 * 1024;
6365
+ var ARCHIVE_RETENTION_BYTES = 64 * 1024 * 1024;
6365
6366
  function loadSession(state) {
6367
+ pruneArchives();
6366
6368
  try {
6367
6369
  const raw = fs22.readFileSync(SESSION_FILE, "utf-8");
6368
6370
  const data = JSON.parse(raw);
@@ -6450,8 +6452,50 @@ function archiveMessages(messages, label, models) {
6450
6452
  }
6451
6453
  fs22.writeFileSync(dest, JSON.stringify(payload), "utf-8");
6452
6454
  log12.info("Session archived", { label, dest, messageCount: messages.length });
6455
+ pruneArchives();
6453
6456
  return dest;
6454
6457
  }
6458
+ function pruneArchives() {
6459
+ try {
6460
+ const entries = fs22.readdirSync(ARCHIVE_DIR).filter((name) => /^(cleared|rotated)-.*\.json$/.test(name));
6461
+ if (entries.length <= 1) {
6462
+ return;
6463
+ }
6464
+ const sortKey = (name) => name.replace(/^(cleared|rotated)-/, "");
6465
+ const archives = entries.map((name) => ({
6466
+ name,
6467
+ size: fs22.statSync(path11.join(ARCHIVE_DIR, name)).size
6468
+ })).sort((a, b) => sortKey(b.name).localeCompare(sortKey(a.name)));
6469
+ let kept = 0;
6470
+ let cut = archives.length;
6471
+ for (let i = 0; i < archives.length; i++) {
6472
+ if (i === 0 || kept + archives[i].size <= ARCHIVE_RETENTION_BYTES) {
6473
+ kept += archives[i].size;
6474
+ } else {
6475
+ cut = i;
6476
+ break;
6477
+ }
6478
+ }
6479
+ let removed = 0;
6480
+ let freed = 0;
6481
+ for (let i = cut; i < archives.length; i++) {
6482
+ try {
6483
+ fs22.unlinkSync(path11.join(ARCHIVE_DIR, archives[i].name));
6484
+ freed += archives[i].size;
6485
+ removed++;
6486
+ } catch {
6487
+ }
6488
+ }
6489
+ if (removed > 0) {
6490
+ log12.info("Session archives pruned", {
6491
+ removed,
6492
+ freedBytes: freed,
6493
+ keptBytes: kept
6494
+ });
6495
+ }
6496
+ } catch {
6497
+ }
6498
+ }
6455
6499
  function rotate(state) {
6456
6500
  const messages = state.messages;
6457
6501
  if (messages.length === 0) {
package/dist/index.js CHANGED
@@ -6717,6 +6717,7 @@ var init_toolResultCap = __esm({
6717
6717
  import fs20 from "fs";
6718
6718
  import path9 from "path";
6719
6719
  function loadSession(state) {
6720
+ pruneArchives();
6720
6721
  try {
6721
6722
  const raw = fs20.readFileSync(SESSION_FILE, "utf-8");
6722
6723
  const data = JSON.parse(raw);
@@ -6804,8 +6805,50 @@ function archiveMessages(messages, label, models) {
6804
6805
  }
6805
6806
  fs20.writeFileSync(dest, JSON.stringify(payload), "utf-8");
6806
6807
  log9.info("Session archived", { label, dest, messageCount: messages.length });
6808
+ pruneArchives();
6807
6809
  return dest;
6808
6810
  }
6811
+ function pruneArchives() {
6812
+ try {
6813
+ const entries = fs20.readdirSync(ARCHIVE_DIR).filter((name) => /^(cleared|rotated)-.*\.json$/.test(name));
6814
+ if (entries.length <= 1) {
6815
+ return;
6816
+ }
6817
+ const sortKey = (name) => name.replace(/^(cleared|rotated)-/, "");
6818
+ const archives = entries.map((name) => ({
6819
+ name,
6820
+ size: fs20.statSync(path9.join(ARCHIVE_DIR, name)).size
6821
+ })).sort((a, b) => sortKey(b.name).localeCompare(sortKey(a.name)));
6822
+ let kept = 0;
6823
+ let cut = archives.length;
6824
+ for (let i = 0; i < archives.length; i++) {
6825
+ if (i === 0 || kept + archives[i].size <= ARCHIVE_RETENTION_BYTES) {
6826
+ kept += archives[i].size;
6827
+ } else {
6828
+ cut = i;
6829
+ break;
6830
+ }
6831
+ }
6832
+ let removed = 0;
6833
+ let freed = 0;
6834
+ for (let i = cut; i < archives.length; i++) {
6835
+ try {
6836
+ fs20.unlinkSync(path9.join(ARCHIVE_DIR, archives[i].name));
6837
+ freed += archives[i].size;
6838
+ removed++;
6839
+ } catch {
6840
+ }
6841
+ }
6842
+ if (removed > 0) {
6843
+ log9.info("Session archives pruned", {
6844
+ removed,
6845
+ freedBytes: freed,
6846
+ keptBytes: kept
6847
+ });
6848
+ }
6849
+ } catch {
6850
+ }
6851
+ }
6809
6852
  function rotate(state) {
6810
6853
  const messages = state.messages;
6811
6854
  if (messages.length === 0) {
@@ -6865,7 +6908,7 @@ function clearSession(state) {
6865
6908
  });
6866
6909
  }
6867
6910
  }
6868
- var log9, SESSION_FILE, ARCHIVE_DIR, ROTATE_THRESHOLD_BYTES, RETAIN_TAIL_BYTES;
6911
+ var log9, SESSION_FILE, ARCHIVE_DIR, ROTATE_THRESHOLD_BYTES, RETAIN_TAIL_BYTES, ARCHIVE_RETENTION_BYTES;
6869
6912
  var init_session = __esm({
6870
6913
  "src/session.ts"() {
6871
6914
  "use strict";
@@ -6878,6 +6921,7 @@ var init_session = __esm({
6878
6921
  ARCHIVE_DIR = ".logs/sessions";
6879
6922
  ROTATE_THRESHOLD_BYTES = 32 * 1024 * 1024;
6880
6923
  RETAIN_TAIL_BYTES = 16 * 1024 * 1024;
6924
+ ARCHIVE_RETENTION_BYTES = 64 * 1024 * 1024;
6881
6925
  }
6882
6926
  });
6883
6927
 
@@ -357,15 +357,24 @@ A verified custom domain (and the app's `madewithremy.com` subdomain) also **sen
357
357
 
358
358
  ```ts
359
359
  {
360
- to: string; // full recipient address; localpart is arbitrary on catchall tiers
361
- from: string; // bare address, extracted from "Name <a@b>" form
362
- subject: string; // 'No Subject' if missing
363
- message: string; // plain text body, falls back to HTML if text is missing; 'No Body' if neither was sent
364
- html: string; // HTML body, or '' when text-only
365
- attachments: string[]; // CDN URLs already uploaded by the platform
360
+ to: string; // full recipient address; localpart is arbitrary on catchall tiers
361
+ from: string; // bare sender address, extracted from "Name <a@b>" form
362
+ fromName: string | null; // sender display name, or null
363
+ subject: string; // 'No Subject' if missing
364
+ message: string; // plain-text body, falls back to HTML if text is missing; 'No Body' if neither was sent
365
+ html: string; // HTML body, or '' when text-only
366
+ attachments: string[]; // CDN URLs — already uploaded by the platform
367
+ messageId: string | null; // this email's Message-ID, angle-bracketed (<id@host>)
368
+ inReplyTo: string | null; // Message-ID this email is replying to, if any
369
+ references: string[]; // prior Message-IDs in the thread (angle-bracketed); [] if none
370
+ replyTo: string | null; // Reply-To address — reply here, not `from`, when set
371
+ cc: string[]; // Cc recipient addresses
372
+ date: string | null; // original send time, ISO-8601
366
373
  }
367
374
  ```
368
375
 
376
+ To reply in-thread, feed these into `sendEmail`: set `inReplyTo` to the incoming `messageId` and `references` to `[...references, messageId]`. Send to `replyTo` when it's set, otherwise `from`. `sendEmail` returns `{ recipients, cc, bcc, from }` (who it sent to + the sender used); it does not return the sent message's own `Message-ID`, so thread off *inbound* mail, not off messages you sent.
377
+
369
378
  ### Attachments and size limits
370
379
 
371
380
  `attachments[]` is an array of CDN URLs — the platform has already received and uploaded the files. Fetch them server-side via the URL when you need the bytes; pass them through as URLs to UI or downstream services.
@@ -80,11 +80,21 @@ const { imageUrl } = await mindstudio.generateImage({
80
80
  prompt: 'A professional headshot placeholder',
81
81
  });
82
82
 
83
- // Send email
83
+ // Send email — own-brand sender auto-selected (custom domain / subdomain / Remy default)
84
84
  await mindstudio.sendEmail({
85
85
  to: 'user@example.com',
86
86
  subject: 'Your invoice',
87
- body: content,
87
+ body: content, // markdown or HTML, auto-detected; bodyType overrides. cc/bcc/replyTo/attachments also supported
88
+ });
89
+
90
+ // Reply in-thread to an inbound email (fields come from the email interface's input)
91
+ await mindstudio.sendEmail({
92
+ to: input.replyTo ?? input.from,
93
+ subject: `Re: ${input.subject}`,
94
+ body: reply,
95
+ inReplyTo: input.messageId ?? undefined,
96
+ references: input.messageId ? [...input.references, input.messageId] : input.references,
97
+ cc: input.cc, // reply-all
88
98
  });
89
99
 
90
100
  // Upload files
@@ -61,7 +61,7 @@ result.$billingCost; // cost in credits (if applicable)
61
61
 
62
62
  | Action | What it does | Key input | Key output |
63
63
  |--------|-------------|-----------|------------|
64
- | `sendEmail` | Send an email | `to`, `subject`, `body` | `messageId` |
64
+ | `sendEmail` | Send an email (own-brand sender auto-selected) | `to`, `subject`, `body`, `cc?`, `bcc?`, `from?`, `replyTo?`, `inReplyTo?`, `references?`, `bodyType?`, `attachments?` | `recipients`, `cc`, `bcc`, `from` |
65
65
  | `sendSMS` | Send a text message | `to`, `message` | `messageId` |
66
66
  | `postToSlackChannel` | Post to Slack | `channel`, `message` | — |
67
67
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mindstudio-ai/remy",
3
- "version": "0.1.227",
3
+ "version": "0.1.229",
4
4
  "description": "Remy coding agent",
5
5
  "repository": {
6
6
  "type": "git",