arcane-os 0.3.6 → 0.4.1

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.
@@ -5,9 +5,9 @@ const MONTH_NUMBER={
5
5
 
6
6
  const MONTH_TOKEN='January|February|March|April|May|June|July|August|September|October|November|December';
7
7
 
8
- function boundedInteger(value,{minimum=0,maximum=10000,fallback=0}={}){
8
+ function normalizedInteger(value,{minimum=0,fallback=0}={}){
9
9
  const number=Number(value);
10
- return Number.isInteger(number)&&number>=minimum&&number<=maximum?number:fallback;
10
+ return Number.isInteger(number)&&number>=minimum?number:fallback;
11
11
  }
12
12
 
13
13
  function textLines(value=''){
@@ -35,16 +35,14 @@ function lineAtOffset(offsets=[],index=0){
35
35
  return Math.max(0,high);
36
36
  }
37
37
 
38
- function cleanExcerpt(lines=[],start=0,end=start,{maximumLength=1200}={}){
39
- const maximum=boundedInteger(maximumLength,{minimum:80,maximum:10000,fallback:1200});
40
- const value=lines.slice(start,end+1)
38
+ function cleanExcerpt(lines=[],start=0,end=start){
39
+ return lines.slice(start,end+1)
41
40
  .filter(line=>!/^\s*(?:```|---)\s*$/.test(line))
42
41
  .map(line=>line.replace(/^\s{0,4}(?:[-*+]\s+|>\s*)?/,'').trim())
43
42
  .filter(Boolean)
44
43
  .join(' ')
45
44
  .replace(/\s+/g,' ')
46
45
  .trim();
47
- return value.length>maximum?`${value.slice(0,maximum-1).trimEnd()}…`:value;
48
46
  }
49
47
 
50
48
  function pageMarkers(lines=[]){
@@ -73,23 +71,18 @@ function globalPattern(pattern){
73
71
  }
74
72
 
75
73
  function passageKey(item={}){
76
- return `${item.ruleId}|${item.page??''}|${item.lineStart}|${item.excerpt.toLocaleLowerCase().replace(/\W+/g,' ').slice(0,180)}`;
74
+ return `${item.ruleId}|${item.page??''}|${item.lineStart}|${item.excerpt.toLocaleLowerCase().replace(/\W+/g,' ')}`;
77
75
  }
78
76
 
79
77
  function findRulePassages(text='',rules=[],{
80
78
  recordId='',
81
- contextLines=2,
82
- maximumExcerptLength=1200,
83
- maximumPerRule=40,
84
- maximumResults=1200
79
+ contextLines=2
85
80
  }={}){
86
81
  const source=String(text??'').replace(/\r\n?/g,'\n');
87
82
  const lines=textLines(source);
88
83
  const offsets=lineOffsets(lines);
89
84
  const markers=pageMarkers(lines);
90
- const defaultContext=boundedInteger(contextLines,{minimum:0,maximum:20,fallback:2});
91
- const perRule=boundedInteger(maximumPerRule,{minimum:1,maximum:500,fallback:40});
92
- const totalLimit=boundedInteger(maximumResults,{minimum:1,maximum:10000,fallback:1200});
85
+ const defaultContext=normalizedInteger(contextLines,{minimum:0,fallback:2});
93
86
  const findings=[];
94
87
  const seen=new Set();
95
88
 
@@ -100,13 +93,12 @@ function findRulePassages(text='',rules=[],{
100
93
  for(const supplied of patterns.filter(Boolean)){
101
94
  const pattern=globalPattern(supplied);
102
95
  for(const match of source.matchAll(pattern)){
103
- if(ruleCount>=perRule||findings.length>=totalLimit) break;
104
96
  const matchIndex=match.index??0;
105
97
  const line=lineAtOffset(offsets,matchIndex);
106
- const localContext=boundedInteger(definition.contextLines,{minimum:0,maximum:20,fallback:defaultContext});
98
+ const localContext=normalizedInteger(definition.contextLines,{minimum:0,fallback:defaultContext});
107
99
  const lineStart=Math.max(0,line-localContext);
108
100
  const lineEnd=Math.min(lines.length-1,line+localContext);
109
- const excerpt=cleanExcerpt(lines,lineStart,lineEnd,{maximumLength:definition.maximumExcerptLength||maximumExcerptLength});
101
+ const excerpt=cleanExcerpt(lines,lineStart,lineEnd);
110
102
  if(!excerpt) continue;
111
103
  const candidate={
112
104
  id:`${String(recordId||'record')}:${String(definition.id)}:${line+1}:${ruleCount+1}`,
@@ -129,7 +121,6 @@ function findRulePassages(text='',rules=[],{
129
121
  ruleCount++;
130
122
  }
131
123
  }
132
- if(findings.length>=totalLimit) break;
133
124
  }
134
125
  return findings;
135
126
  }
@@ -162,16 +153,13 @@ function parseDateMention(value=''){
162
153
 
163
154
  function extractDateMentions(text='',{
164
155
  recordId='',
165
- contextLines=1,
166
- maximumExcerptLength=900,
167
- maximumResults=2000
156
+ contextLines=1
168
157
  }={}){
169
158
  const source=String(text??'').replace(/\r\n?/g,'\n');
170
159
  const lines=textLines(source);
171
160
  const offsets=lineOffsets(lines);
172
161
  const markers=pageMarkers(lines);
173
- const context=boundedInteger(contextLines,{minimum:0,maximum:20,fallback:1});
174
- const limit=boundedInteger(maximumResults,{minimum:1,maximum:20000,fallback:2000});
162
+ const context=normalizedInteger(contextLines,{minimum:0,fallback:1});
175
163
  const patterns=[
176
164
  new RegExp(`\\b(?:${MONTH_TOKEN})\\s+\\d{1,2}(?:st|nd|rd|th)?,?\\s+\\d{4}\\b`,'gi'),
177
165
  /\b\d{4}-\d{1,2}-\d{1,2}\b/g,
@@ -183,7 +171,6 @@ function extractDateMentions(text='',{
183
171
  const results=[];
184
172
  for(const pattern of patterns){
185
173
  for(const match of source.matchAll(pattern)){
186
- if(results.length>=limit) break;
187
174
  const start=match.index??0; const end=start+match[0].length;
188
175
  if(occupied.some(range=>start<range.end&&end>range.start)) continue;
189
176
  const parsed=parseDateMention(match[0]);
@@ -202,10 +189,9 @@ function extractDateMentions(text='',{
202
189
  lineStart:lineStart+1,
203
190
  lineEnd:lineEnd+1,
204
191
  page:pageAtLine(markers,line),
205
- excerpt:cleanExcerpt(lines,lineStart,lineEnd,{maximumLength:maximumExcerptLength})
192
+ excerpt:cleanExcerpt(lines,lineStart,lineEnd)
206
193
  });
207
194
  }
208
- if(results.length>=limit) break;
209
195
  }
210
196
  return results.sort((left,right)=>left.isoDate.localeCompare(right.isoDate)||left.lineStart-right.lineStart);
211
197
  }
@@ -148,7 +148,14 @@ function recordMap(records){
148
148
  }
149
149
 
150
150
  function normalizedStoredRecords(value){
151
- if(!isPlainRecord(value)) return {};
151
+ if(!isPlainRecord(value)){
152
+ throw recordReviewStoreError(
153
+ RECORD_REVIEW_STORE_ERROR_CODES.storedRecordsInvalid,
154
+ 'record-review-stored-records-invalid',
155
+ 'Stored record reviews must be a plain object.',
156
+ TypeError
157
+ );
158
+ }
152
159
  const records={};
153
160
  for(const [recordId,review] of Object.entries(value)){
154
161
  let id;
@@ -196,8 +203,14 @@ function localAdapter(namespace){
196
203
  if(!raw) return {};
197
204
  try{
198
205
  return JSON.parse(raw);
199
- }catch{
200
- return {};
206
+ }catch(error){
207
+ throw recordReviewStoreError(
208
+ RECORD_REVIEW_STORE_ERROR_CODES.storedRecordsInvalid,
209
+ 'record-review-stored-json-invalid',
210
+ 'Stored record reviews contain invalid JSON.',
211
+ TypeError,
212
+ error
213
+ );
201
214
  }
202
215
  },
203
216
  async set(value){
@@ -1,7 +1,7 @@
1
1
  import {analyzeRiskSignals} from './RiskSignalAnalyzer.js';
2
2
  import {canonicalNetworkHostname,emptyArcaneNetworkPolicy,findDeniedDomainRule,loadArcaneNetworkPolicy} from './ArcaneNetworkPolicy.js?v=3';
3
3
 
4
- export const scamRiskSignals=Object.freeze([
4
+ export const scamRiskSignals=[
5
5
  {id:'urgency',label:'Urgency or secrecy pressure',weight:18,pattern:/\b(urgent|immediately|act now|do not tell|keep (?:this|it) secret|stay on the line)\b/i,guidance:'Pause. A legitimate organization will let you verify independently.'},
6
6
  {id:'payment',label:'Unusual payment request',weight:32,pattern:/\b(gift cards?|bitcoin|crypto(?:currency)?|wire transfer|cash courier|payment app|prepaid cards?)\b/i,guidance:'Do not pay. Contact the organization using a trusted number.'},
7
7
  {id:'credential',label:'Credential or access request',weight:35,pattern:/\b(password|passcode|verification code|one[- ]time code|otp|remote access|screen share)\b/i,guidance:'Never share security codes or grant remote access to an unexpected contact.'},
@@ -9,20 +9,20 @@ export const scamRiskSignals=Object.freeze([
9
9
  {id:'threat',label:'Threat or fear tactic',weight:28,pattern:/\b(arrest|warrant|deport|account (?:will be )?closed|service (?:will be )?cut off|in danger|kidnapped)\b/i,guidance:'Threats are designed to prevent careful checking. Stop and contact someone you trust.'},
10
10
  {id:'prize',label:'Unexpected prize or refund',weight:20,pattern:/\b(lottery|sweepstakes|prize|inheritance|refund|you(?: have|'ve) won)\b/i,guidance:'Do not pay a fee or disclose information to receive an unexpected benefit.'},
11
11
  {id:'link',label:'Pressure to open a link',weight:16,pattern:/\b(click|tap|open|visit|follow)\b.{0,36}\b(link|url|website)\b/i,guidance:'Do not use an unexpected link. Open the official app or type a trusted address yourself.'},
12
- ]);
12
+ ];
13
13
 
14
- const blockedDomainSignal=Object.freeze({
14
+ const blockedDomainSignal={
15
15
  id:'blocked-domain',
16
16
  label:'Domain blocked by Arcane network policy',
17
17
  weight:55,
18
18
  guidance:'Do not open or contact this domain. Arcane has a system-wide safety rule blocking it.',
19
- });
19
+ };
20
20
  let activeNetworkPolicy=emptyArcaneNetworkPolicy();
21
21
  let activeNetworkPolicyLoadSequence=0;
22
22
  let nextNetworkPolicyLoadSequence=0;
23
23
 
24
24
  function candidateHostnames(value){
25
- const text=String(value??'').normalize('NFKC').slice(0,20_000),hostnames=new Set();
25
+ const text=String(value??'').normalize('NFKC'),hostnames=new Set();
26
26
  const add=value=>{
27
27
  const candidate=String(value).replace(/^[([{<'"]+|[\])}>'",.;!?]+$/g,'');
28
28
  try{
@@ -46,13 +46,14 @@ export async function loadScamNetworkPolicy(options){
46
46
  return networkPolicy;
47
47
  }
48
48
 
49
- export function assessScamRisk(text,{networkPolicy=activeNetworkPolicy}={}){
49
+ export function assessScamRisk(text,{networkPolicy=activeNetworkPolicy,secure=false}={}){
50
50
  const result=analyzeRiskSignals(text,{signals:scamRiskSignals});
51
+ if(secure!==true)return result;
51
52
  let blocked=false;
52
53
  for(const hostname of candidateHostnames(text)){if(findDeniedDomainRule(networkPolicy,hostname)){blocked=true;break;}}
53
54
  if(!blocked)return result;
54
55
  const score=Math.min(100,result.score+blockedDomainSignal.weight);
55
- return Object.freeze({...result,score,level:levelForScore(score),matches:Object.freeze([...result.matches,blockedDomainSignal])});
56
+ return {...result,score,level:levelForScore(score),matches:[...result.matches,blockedDomainSignal]};
56
57
  }
57
58
 
58
59
  export function scamSafetyGuidance(result){
@@ -6,7 +6,6 @@ export const IMPORT_MAP_RELATIVE_PATH='modules/arcane.importmap.json';
6
6
  export const MANAGED_IMPORT_MAP_ATTRIBUTE='data-arcane-import-map';
7
7
 
8
8
  const JAVASCRIPT_EXTENSION=/\.(?:js|mjs)$/u;
9
- const NODE_ONLY_MODULE='modules/CaseEvidenceIndexer.js';
10
9
  const PERSISTENT_CHAT_IMPORT='#arcane/persistent-ai-chat-session';
11
10
  const PERSISTENT_CHAT_MODULE='modules/PersistentAIChatSession.js';
12
11
  const SDK_BROWSER_ENTRY='sdk/event-manager.mjs';
@@ -808,13 +807,8 @@ export async function buildImportMap({files,signal}={}){
808
807
  &&JAVASCRIPT_EXTENSION.test(relative))
809
808
  .sort(compareText);
810
809
  const namedRegistry=new Map();
811
- const excludedModules=[];
812
810
  for(const relative of modules){
813
811
  throwIfAborted(signal);
814
- if(relative===NODE_ONLY_MODULE){
815
- excludedModules.push(relative);
816
- continue;
817
- }
818
812
  const name=path.posix.basename(relative).replace(JAVASCRIPT_EXTENSION,'');
819
813
  registerSpecifier(namedRegistry,`arcane/${name}`,`./arcane/${relative}`);
820
814
  }
@@ -870,7 +864,7 @@ export async function buildImportMap({files,signal}={}){
870
864
  }
871
865
  return {
872
866
  imports,
873
- excludedModules:excludedModules.sort(compareText)
867
+ excludedModules:[]
874
868
  };
875
869
  }
876
870