arcane-os 0.3.0 → 0.3.2

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 (153) hide show
  1. package/CHANGELOG.md +27 -0
  2. package/README.md +86 -117
  3. package/bin/arcane-test.mjs +170 -46
  4. package/browser-runtime/ai/browser-speech-artifacts.mjs +887 -909
  5. package/browser-runtime/ai/browser-speech-providers.mjs +96 -152
  6. package/browser-runtime/ai/browser-wasm-llm-provider.mjs +627 -819
  7. package/browser-runtime/ai/browser-wasm.mjs +24 -35
  8. package/browser-runtime/ai/browser-wllama-runtime.mjs +64 -316
  9. package/browser-runtime/ai/model-controller.mjs +584 -181
  10. package/browser-runtime/ai/speech-worker-client.mjs +8 -146
  11. package/browser-runtime/ai/speech-worker-runtime.mjs +643 -363
  12. package/browser-runtime/dom-event-instrumentation.mjs +55 -147
  13. package/browser-runtime/event-manager.mjs +239 -624
  14. package/package.json +5 -6
  15. package/runtime/arcane/components/app-bar.html +3 -15
  16. package/runtime/arcane/components/assistant-panel.html +10 -10
  17. package/runtime/arcane/components/calculator.html +1 -1
  18. package/runtime/arcane/components/chat.html +1359 -135
  19. package/runtime/arcane/components/conversation-view.html +2 -2
  20. package/runtime/arcane/components/document-inspector.html +11 -17
  21. package/runtime/arcane/components/file-manager.html +13 -56
  22. package/runtime/arcane/components/markdown-document.html +82 -281
  23. package/runtime/arcane/components/markdown-editor.html +7 -10
  24. package/runtime/arcane/components/media-embed.html +6 -6
  25. package/runtime/arcane/components/screen-capture.html +4 -4
  26. package/runtime/arcane/components/source-explanation.html +2 -2
  27. package/runtime/arcane/components/speech.html +112 -68
  28. package/runtime/arcane/components/terminal-workspace.html +4 -4
  29. package/runtime/arcane/components/theme-editor.html +1 -1
  30. package/runtime/arcane/components/unified-inbox.html +2 -2
  31. package/runtime/arcane/components/voice-transcription.html +31 -21
  32. package/runtime/arcane/entities/Calculation.js +2 -3
  33. package/runtime/arcane/entities/Chat.js +228 -43
  34. package/runtime/arcane/entities/Preference.js +3 -5
  35. package/runtime/arcane/entities/Weather.js +5 -5
  36. package/runtime/arcane/modules/AI.js +1050 -427
  37. package/runtime/arcane/modules/AIProviderRuntime.js +658 -363
  38. package/runtime/arcane/modules/AIResponseLength.js +9 -19
  39. package/runtime/arcane/modules/AIRuntimeState.js +109 -72
  40. package/runtime/arcane/modules/ArcaneNavigationPolicy.js +45 -32
  41. package/runtime/arcane/modules/BrowserTestSuite.js +78 -122
  42. package/runtime/arcane/modules/CalculatorEngine.js +9 -9
  43. package/runtime/arcane/modules/CommunicationAppController.js +3 -7
  44. package/runtime/arcane/modules/ComponentContracts.js +30 -32
  45. package/runtime/arcane/modules/ConfiguredAIChatSession.js +281 -230
  46. package/runtime/arcane/modules/ConversationActionItems.js +26 -59
  47. package/runtime/arcane/modules/ConversationClosingReport.js +34 -61
  48. package/runtime/arcane/modules/ConversationTimebox.js +27 -15
  49. package/runtime/arcane/modules/DBOPFSDocumentLibrary.js +152 -344
  50. package/runtime/arcane/modules/DocumentLexicalSearch.js +25 -91
  51. package/runtime/arcane/modules/HTMLImport.js +54 -1
  52. package/runtime/arcane/modules/IsolatedModelQuestionRunner.js +40 -203
  53. package/runtime/arcane/modules/LocalAIReadiness.js +40 -60
  54. package/runtime/arcane/modules/LocalAIReadinessController.js +15 -13
  55. package/runtime/arcane/modules/MD.js +1 -45
  56. package/runtime/arcane/modules/Mail.js +51 -103
  57. package/runtime/arcane/modules/MailOutbox.mjs +95 -193
  58. package/runtime/arcane/modules/MailTransport.mjs +36 -57
  59. package/runtime/arcane/modules/ModelDefinition.js +22 -106
  60. package/runtime/arcane/modules/OpenMeteoWeatherProvider.js +39 -101
  61. package/runtime/arcane/modules/PersistentAIChatSession.js +281 -18
  62. package/runtime/arcane/modules/PreferenceStore.js +102 -30
  63. package/runtime/arcane/modules/RiskSignalAnalyzer.js +8 -9
  64. package/runtime/arcane/modules/ScopedOPFSCache.js +7 -42
  65. package/runtime/arcane/modules/ScreenCapture.js +175 -128
  66. package/runtime/arcane/modules/SpeechPlayback.js +46 -149
  67. package/runtime/arcane/modules/StaticDocumentCatalog.js +173 -407
  68. package/runtime/arcane/modules/ToolCallRouter.js +25 -12
  69. package/runtime/arcane/modules/YouTubeMedia.js +6 -5
  70. package/schemas/arcane-app-bundle.schema.json +13 -78
  71. package/schemas/arcane-app.schema.json +9 -25
  72. package/schemas/arcane-lock.schema.json +18 -151
  73. package/schemas/arcane-package.schema.json +2 -16
  74. package/schemas/native-build-plan.schema.json +119 -122
  75. package/src/app-descriptor.mjs +75 -132
  76. package/src/application-tests.mjs +200 -0
  77. package/src/cli/main.mjs +27 -46
  78. package/src/constants.mjs +3 -4
  79. package/src/dev-server.mjs +30 -324
  80. package/src/doctor.mjs +92 -154
  81. package/src/dom-event-instrumentation.mjs +55 -147
  82. package/src/errors.mjs +2 -3
  83. package/src/event-manager.mjs +239 -624
  84. package/src/event-queue.mjs +3 -3
  85. package/src/import-map.mjs +273 -1028
  86. package/src/index.mjs +14 -16
  87. package/src/installed-sdk-runtime.mjs +40 -62
  88. package/src/integrated-provider-loader.mjs +53 -382
  89. package/src/mail-api.mjs +0 -2
  90. package/src/mail-server.mjs +224 -580
  91. package/src/mail.mjs +4 -10
  92. package/src/native-plan.mjs +163 -598
  93. package/src/native-provider-loader.mjs +104 -1063
  94. package/src/packager/core.mjs +485 -3229
  95. package/src/process.mjs +5 -10
  96. package/src/release-bundle.mjs +292 -2405
  97. package/src/runtime.mjs +76 -396
  98. package/src/scaffold.mjs +30 -80
  99. package/src/sdk-browser-runtime.mjs +70 -626
  100. package/src/source-server.mjs +588 -0
  101. package/src/targets/index.mjs +78 -188
  102. package/src/templates/workspace-template.mjs +19 -135
  103. package/src/testing-loader.mjs +164 -0
  104. package/src/testing.mjs +1 -1
  105. package/src/toolchain.mjs +131 -544
  106. package/src/update-check.mjs +26 -64
  107. package/src/workspace-operation-lock.mjs +139 -430
  108. package/src/workspace-runtime.mjs +112 -779
  109. package/src/workspace.mjs +40 -302
  110. package/browser-runtime/ARCANE_SDK_BROWSER_RELEASE.json +0 -218
  111. package/browser-runtime/ai/ARCANE_AI_BROWSER_SPEECH_COMPONENTS.json +0 -203
  112. package/browser-runtime/ai/ARCANE_AI_BROWSER_WASM_COMPONENTS.json +0 -80
  113. package/browser-runtime/ai/internal/sha256.mjs +0 -166
  114. package/docs/architecture.md +0 -344
  115. package/docs/compatibility.md +0 -36
  116. package/docs/event-manager.md +0 -294
  117. package/docs/platform-targets.md +0 -108
  118. package/docs/publishing.md +0 -201
  119. package/docs/reference/README.md +0 -187
  120. package/docs/reference/ai/browser-speech-package-authority.json +0 -835
  121. package/docs/reference/ai/browser-speech.md +0 -1252
  122. package/docs/reference/ai/browser-wasm.md +0 -530
  123. package/docs/reference/arcane-ollama.md +0 -288
  124. package/docs/reference/availability-and-normalization.md +0 -183
  125. package/docs/reference/behavioral-testing.md +0 -133
  126. package/docs/reference/cli.md +0 -779
  127. package/docs/reference/core/README.md +0 -62
  128. package/docs/reference/core/arcane-ai-contracts.md +0 -907
  129. package/docs/reference/core/arcane-api.md +0 -601
  130. package/docs/reference/core/arcane-entities.md +0 -65
  131. package/docs/reference/core/arcane-events.md +0 -134
  132. package/docs/reference/core/ollama-module.md +0 -181
  133. package/docs/reference/core/reference/arcane-api/ai-and-ollama.md +0 -1909
  134. package/docs/reference/core/reference/arcane-api/applications-terminal-capabilities.md +0 -1057
  135. package/docs/reference/core/reference/arcane-api/core-and-events.md +0 -320
  136. package/docs/reference/core/reference/arcane-api/filesystem-storage-preferences-appearance.md +0 -610
  137. package/docs/reference/core/reference/arcane-api/namespaces.md +0 -1157
  138. package/docs/reference/core/reference/arcane-api/platform-installation-users-system.md +0 -1423
  139. package/docs/reference/core/reference/arcane-api/session-provisioning-diagnostics-development.md +0 -315
  140. package/docs/reference/event-manager.md +0 -1511
  141. package/docs/reference/inventory/package-api.json +0 -3284
  142. package/docs/reference/inventory/runtime-components.json +0 -1011
  143. package/docs/reference/inventory/runtime-entities.json +0 -26
  144. package/docs/reference/inventory/runtime-modules.json +0 -1431
  145. package/docs/reference/mail.md +0 -316
  146. package/docs/reference/protocols.md +0 -677
  147. package/docs/reference/runtime-components.md +0 -1366
  148. package/docs/reference/runtime-entities.md +0 -303
  149. package/docs/reference/runtime-modules.md +0 -2960
  150. package/docs/reference/sdk-api.md +0 -6694
  151. package/docs/roadmap.md +0 -79
  152. package/docs/work-amplification.md +0 -129
  153. package/runtime/ARCANE_RUNTIME_RELEASE.json +0 -826
@@ -7,29 +7,24 @@ import {
7
7
 
8
8
  let userInstance=null;
9
9
 
10
- const MAX_SUBJECT_LENGTH=160;
11
- const MAX_MESSAGE_BYTES=25*1024*1024;
12
- const MAX_NATIVE_REPORT_BYTES=768*1024;
10
+ function completeResult(value){return value;}
11
+
13
12
  const MAIL_TYPES=new Set(['error','report','crisis_detected']);
14
- const MAIL_OUTBOX_EVENTS=Object.freeze([
13
+ const MAIL_OUTBOX_EVENTS=completeResult([
15
14
  'mail-outbox-state',
16
15
  'mail-outbox-delivery',
17
16
  'mail-outbox-drain'
18
17
  ]);
19
18
  const PENDING_OUTBOX_STATES=new Set(['queued','sending','retry_wait']);
20
- const NATIVE_MAIL_RESPONSE_STATUS_CODES=Object.freeze({
19
+ const NATIVE_MAIL_RESPONSE_STATUS_CODES=completeResult({
21
20
  accepted:202,
22
21
  delivery_uncertain:207,
23
22
  partially_accepted:207
24
23
  });
25
- const NATIVE_MAIL_RESULT_KEYS=Object.freeze([
26
- 'partial','requestId','sent','status','statusCode','uncertain'
27
- ]);
28
- const NATIVE_MAIL_REQUEST_ID_PATTERN=/^[A-Za-z0-9-]{8,128}$/;
24
+ const NATIVE_MAIL_REQUEST_ID_PATTERN=/^[A-Za-z0-9-]+$/;
29
25
  const NATIVE_MAIL_UNCERTAIN_ERROR_CODES=new Set([
30
26
  'ARCANE_REQUEST_TIMEOUT',
31
27
  'MAIL_GATEWAY_RESPONSE_INVALID',
32
- 'MAIL_GATEWAY_RESPONSE_TOO_LARGE',
33
28
  'MAIL_GATEWAY_UNAVAILABLE',
34
29
  'MAIL_NATIVE_RESULT_INVALID',
35
30
  'MAIL_SEND_TIMEOUT',
@@ -74,10 +69,10 @@ function linkedMailSignal(primary,secondary){
74
69
  if(signal&&!signals.includes(signal)) signals.push(signal);
75
70
  }
76
71
  if(signals.length===0){
77
- return Object.freeze({signal:null,dispose:function disposeEmptyMailSignal(){}});
72
+ return completeResult({signal:null,dispose:function disposeEmptyMailSignal(){}});
78
73
  }
79
74
  if(signals.length===1){
80
- return Object.freeze({signal:signals[0],dispose:function disposeSingleMailSignal(){}});
75
+ return completeResult({signal:signals[0],dispose:function disposeSingleMailSignal(){}});
81
76
  }
82
77
  const controller=new AbortController();
83
78
  const listeners=[];
@@ -89,7 +84,7 @@ function linkedMailSignal(primary,secondary){
89
84
  if(signal.aborted) listener();
90
85
  else signal.addEventListener('abort',listener,{once:true});
91
86
  }
92
- return Object.freeze({
87
+ return completeResult({
93
88
  signal:controller.signal,
94
89
  dispose:function disposeLinkedMailSignal(){
95
90
  for(const entry of listeners){
@@ -129,7 +124,6 @@ function waitForMailOperation(operation,signal){
129
124
 
130
125
  function exactNativeMailResult(value){
131
126
  if(!value||typeof value!=='object'||Array.isArray(value)
132
- ||Object.keys(value).sort().join(',')!==[...NATIVE_MAIL_RESULT_KEYS].sort().join(',')
133
127
  ||typeof value.requestId!=='string'
134
128
  ||!NATIVE_MAIL_REQUEST_ID_PATTERN.test(value.requestId)
135
129
  ||!Object.hasOwn(NATIVE_MAIL_RESPONSE_STATUS_CODES,value.status)
@@ -157,12 +151,15 @@ function normalizedNativeMailError(error){
157
151
  const retryable=uncertain||NATIVE_MAIL_RETRYABLE_ERROR_CODES.has(error?.code);
158
152
  if(!retryable) return error;
159
153
  const normalized=new Error(
160
- uncertain
161
- ?'Native Arcane mail delivery has an uncertain outcome.'
162
- :'Native Arcane mail transport is temporarily unavailable.',
154
+ typeof error?.message==='string'&&error.message
155
+ ?error.message
156
+ :uncertain
157
+ ?'Native Arcane mail delivery has an uncertain outcome.'
158
+ :'Native Arcane mail transport is temporarily unavailable.',
163
159
  {cause:error}
164
160
  );
165
- normalized.code=error.code;
161
+ Object.assign(normalized,error);
162
+ normalized.code=error?.code;
166
163
  normalized.retryable=true;
167
164
  normalized.uncertain=uncertain;
168
165
  const statusCode=Number(error?.statusCode??error?.status);
@@ -253,7 +250,7 @@ export function resolveMailConfig(
253
250
  const appName=typeof supplied.appName==='string'&&supplied.appName.trim()
254
251
  ? supplied.appName.trim()
255
252
  : declaredApplicationId(document);
256
- return Object.freeze({
253
+ return completeResult({
257
254
  appName:ARCANE_APP_ID_PATTERN.test(appName) ? appName:'',
258
255
  appKey:typeof supplied.appKey==='string' ? supplied.appKey:'',
259
256
  endpoint:typeof supplied.endpoint==='string'&&supplied.endpoint.trim()
@@ -274,10 +271,6 @@ function escapeHtml(value){
274
271
  .replaceAll("'",''');
275
272
  }
276
273
 
277
- function utf8ByteLength(value){
278
- return new TextEncoder().encode(value).byteLength;
279
- }
280
-
281
274
  function clonePayload(value){
282
275
  if(typeof globalThis.structuredClone==='function'){
283
276
  return globalThis.structuredClone(value);
@@ -288,28 +281,18 @@ function clonePayload(value){
288
281
  function serializePayload(value){
289
282
  try{
290
283
  return JSON.stringify(value,null,2);
291
- }catch{
292
- return '[The structured payload could not be serialized.]';
284
+ }catch(error){
285
+ const failure=codedError(
286
+ 'Mail payload must be completely JSON serializable.',
287
+ 'MAIL_PAYLOAD_NOT_SERIALIZABLE',
288
+ TypeError
289
+ );
290
+ failure.cause=error;
291
+ throw failure;
293
292
  }
294
293
  }
295
294
 
296
- function assertMessageSize(report){
297
- const messageBytes=['text','html'].reduce(
298
- function sumMessageBytes(total,key){
299
- return total+utf8ByteLength(report[key]||'');
300
- },
301
- 0
302
- );
303
- if(messageBytes>MAX_MESSAGE_BYTES){
304
- throw new Error('Generated report email content exceeds the 25 MiB limit');
305
- }
306
- }
307
-
308
- function assertNativeReportSize(serializedReport){
309
- if(utf8ByteLength(serializedReport)>MAX_NATIVE_REPORT_BYTES){
310
- throw new RangeError('Native Arcane mail reports cannot exceed 786,432 serialized bytes');
311
- }
312
- }
295
+ let reportNonceSequence=0;
313
296
 
314
297
  function randomReportNonce(cryptoProvider){
315
298
  if(typeof cryptoProvider?.randomUUID==='function'){
@@ -320,19 +303,8 @@ function randomReportNonce(cryptoProvider){
320
303
  }
321
304
  }catch{}
322
305
  }
323
- if(typeof cryptoProvider?.getRandomValues==='function'){
324
- try{
325
- const bytes=new Uint8Array(16);
326
- cryptoProvider.getRandomValues(bytes);
327
- return [...bytes].map(function reportNonceByte(value){
328
- return value.toString(16).padStart(2,'0');
329
- }).join('');
330
- }catch{}
331
- }
332
- throw codedError(
333
- 'Mail requires Web Crypto to create a durable idempotency key.',
334
- 'MAIL_CRYPTO_UNAVAILABLE'
335
- );
306
+ reportNonceSequence+=1;
307
+ return `${Date.now().toString(36)}-${reportNonceSequence.toString(36)}`;
336
308
  }
337
309
 
338
310
  function createReportKey(timestamp,cryptoProvider){
@@ -343,13 +315,8 @@ function normalizeRecipients(values){
343
315
  if(!Array.isArray(values)){
344
316
  throw new TypeError('Mail recipients must be an array');
345
317
  }
346
- if(values.length>50){
347
- throw new TypeError('Mail supports no more than 50 recipients');
348
- }
349
-
350
318
  const recipients=[];
351
319
  for(const value of values){
352
- if(!value) continue;
353
320
  if(typeof value!=='string'){
354
321
  throw new TypeError('Every mail recipient must be an email address');
355
322
  }
@@ -357,7 +324,7 @@ function normalizeRecipients(values){
357
324
  if(address.length>254||!EMAIL_PATTERN.test(address)){
358
325
  throw new TypeError('Mail contains an invalid recipient address');
359
326
  }
360
- if(!recipients.includes(address)) recipients.push(address);
327
+ recipients.push(address);
361
328
  }
362
329
  return recipients;
363
330
  }
@@ -401,7 +368,9 @@ function normalizedDeliveryResult(value){
401
368
 
402
369
  function publicSendResult(record){
403
370
  return {
371
+ ...record,
404
372
  ...(record.result||{}),
373
+ report:JSON.parse(record.serializedReport),
405
374
  reportKey:record.reportKey,
406
375
  state:record.state,
407
376
  status:record.result?.status||record.state,
@@ -413,15 +382,11 @@ function publicSendResult(record){
413
382
  }
414
383
 
415
384
  function safeDrainDetail(summary){
416
- return Object.freeze({
417
- reason:summary.reason,
418
- online:summary.online,
419
- considered:summary.considered,
420
- attempted:summary.attempted,
421
- invalidRecords:summary.invalidRecords,
422
- bounded:summary.bounded,
423
- pending:summary.pending,
424
- states:Object.freeze({...summary.states})
385
+ return completeResult({
386
+ ...summary,
387
+ records:[...summary.records],
388
+ invalidRecords:[...summary.invalidRecords],
389
+ states:completeResult({...summary.states})
425
390
  });
426
391
  }
427
392
 
@@ -468,13 +433,7 @@ function normalizeMailOptions(options){
468
433
  isOnline,
469
434
  onlineTarget,
470
435
  outboxOptions:{
471
- ...(options.maxAttemptsPerDrain===undefined
472
- ?{}:{maxAttemptsPerDrain:options.maxAttemptsPerDrain}),
473
- ...(options.maxInvalidRecords===undefined
474
- ?{}:{maxInvalidRecords:options.maxInvalidRecords}),
475
- ...(options.maxRecords===undefined?{}:{maxRecords:options.maxRecords}),
476
436
  ...(options.lockManager===undefined?{}:{lockManager:options.lockManager}),
477
- maxReportBytes:options.maxReportBytes??MAX_MESSAGE_BYTES,
478
437
  },
479
438
  storage:options.storage,
480
439
  storageInjected:Object.hasOwn(options,'storage'),
@@ -563,7 +522,7 @@ class Mail {
563
522
  get disposed(){return this.#disposed;}
564
523
  get events(){return this.#events;}
565
524
  get invalidOutboxRecords(){
566
- return this.#outbox?.invalidRecords??Object.freeze([]);
525
+ return this.#outbox?.invalidRecords??[];
567
526
  }
568
527
  get lastBackgroundError(){return this.#backgroundError;}
569
528
 
@@ -607,16 +566,14 @@ class Mail {
607
566
  }
608
567
 
609
568
  #publishState(detail,operationId=null){
610
- this.#publish('mail-outbox-state',Object.freeze({...detail}),operationId);
569
+ this.#publish('mail-outbox-state',{...detail},operationId);
611
570
  }
612
571
 
613
572
  #publishRecord(record){
614
- const stateDetail=Object.freeze({
615
- reportKey:record.reportKey,
616
- state:record.state,
617
- attempts:record.attempts,
573
+ const stateDetail={
574
+ ...record,
618
575
  pending:PENDING_OUTBOX_STATES.has(record.state)
619
- });
576
+ };
620
577
  this.#publish('mail-outbox-state',stateDetail,record.reportKey);
621
578
  if(record.attempts<1||record.state==='queued'||record.state==='sending') return;
622
579
  const outcome={
@@ -626,12 +583,11 @@ class Mail {
626
583
  reconciliation_required:'reconciliation_required'
627
584
  }[record.state];
628
585
  if(!outcome) return;
629
- this.#publish('mail-outbox-delivery',Object.freeze({
630
- reportKey:record.reportKey,
631
- state:record.state,
586
+ this.#publish('mail-outbox-delivery',{
587
+ ...record,
632
588
  outcome,
633
589
  uncertain:record.failure?.uncertain===true
634
- }),record.reportKey);
590
+ },record.reportKey);
635
591
  }
636
592
 
637
593
  #handleOutboxRecord(record){
@@ -652,7 +608,6 @@ class Mail {
652
608
  ...(request.signal?{signal:request.signal}:{})
653
609
  });
654
610
  }
655
- assertNativeReportSize(request.serializedReport);
656
611
  try{
657
612
  const result=await globalThis.Arcane.mail.send({
658
613
  report:request.report,
@@ -821,8 +776,8 @@ class Mail {
821
776
  return (await this.#getOutbox()).repairInvalid(fileName,replacement);
822
777
  }
823
778
 
824
- async quarantineInvalidOutbox(options){
825
- return (await this.#getOutbox()).quarantineInvalid(options);
779
+ async quarantineInvalidOutbox(){
780
+ return (await this.#getOutbox()).quarantineInvalid();
826
781
  }
827
782
 
828
783
  stop(){
@@ -857,10 +812,10 @@ class Mail {
857
812
 
858
813
  async send(to=[], subject='', payload={}, messageStyle='', messageType='') {
859
814
  this.#assertActive();
860
- const normalizedSubject=typeof subject==='string' ? subject.trim():'';
861
- if(!normalizedSubject||normalizedSubject.length>MAX_SUBJECT_LENGTH||/[\u0000-\u001f\u007f]/.test(normalizedSubject)){
862
- throw new TypeError(`Mail subject must contain 1-${MAX_SUBJECT_LENGTH} characters without line breaks`);
815
+ if(typeof subject!=='string'){
816
+ throw new TypeError('Mail subject must be a string');
863
817
  }
818
+ const normalizedSubject=subject;
864
819
  if(!payload||typeof payload!=='object'||Array.isArray(payload)){
865
820
  throw new TypeError('Mail payload must be an object');
866
821
  }
@@ -904,7 +859,6 @@ class Mail {
904
859
  to:recipients,
905
860
  type:messageType,
906
861
  };
907
- assertMessageSize(reportPayload.report);
908
862
  const record=await outbox.enqueue(
909
863
  {report:reportPayload.report,reportKey},
910
864
  {signal}
@@ -933,14 +887,9 @@ class Mail {
933
887
  ? `<pre>${escapeHtml(serialized)}</pre>`
934
888
  : serialized;
935
889
 
936
- const sourceUser=String(reportPayload.source_user||'')
937
- .replace(/[\u0000-\u001f\u007f]/g,' ')
938
- .trim()
939
- .slice(0,80);
940
- const subjectSuffix=sourceUser ? ` - ${sourceUser}`:'';
941
- const deliverySubject=normalizedSubject.length+subjectSuffix.length<=MAX_SUBJECT_LENGTH
942
- ? `${normalizedSubject}${subjectSuffix}`
943
- : normalizedSubject;
890
+ const sourceUser=String(reportPayload.source_user||'');
891
+ const subjectSuffix=sourceUser?` - ${sourceUser}`:'';
892
+ const deliverySubject=`${normalizedSubject}${subjectSuffix}`;
944
893
 
945
894
  reportPayload.report = {
946
895
  subject:deliverySubject,
@@ -960,7 +909,6 @@ Email: ${profile.email || 'not provided'}`
960
909
  :generatedMessage;
961
910
  }
962
911
 
963
- assertMessageSize(reportPayload.report);
964
912
  throwIfMailAborted(signal);
965
913
  const record=await outbox.enqueue(
966
914
  {report:reportPayload.report,reportKey},