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.
- package/CHANGELOG.md +30 -0
- package/README.md +7 -7
- package/package.json +1 -1
- package/runtime/arcane/components/chat.html +13 -5
- package/runtime/arcane/components/directory-picker.html +1 -5
- package/runtime/arcane/components/file-drop.html +1 -12
- package/runtime/arcane/components/record-timeline.html +13 -11
- package/runtime/arcane/components/relationship-board.html +6 -6
- package/runtime/arcane/components/source-code-viewer.html +23 -27
- package/runtime/arcane/modules/ComponentContracts.js +2 -4
- package/runtime/arcane/modules/DevelopmentWorkspace.js +6 -10
- package/runtime/arcane/modules/DirectoryPicker.js +24 -40
- package/runtime/arcane/modules/MessageAdvisory.js +16 -14
- package/runtime/arcane/modules/RecordPassageIndex.js +12 -26
- package/runtime/arcane/modules/RecordReviewStore.js +16 -3
- package/runtime/arcane/modules/ScamRiskPolicy.js +8 -7
- package/src/import-map.mjs +1 -7
- package/runtime/arcane/entities/TWiNPolicyDecision.js +0 -995
- package/runtime/arcane/modules/CaseEvidenceIndexer.js +0 -130
- package/runtime/arcane/modules/RevocableProjectionLedger.js +0 -1623
|
@@ -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
|
|
8
|
+
function normalizedInteger(value,{minimum=0,fallback=0}={}){
|
|
9
9
|
const number=Number(value);
|
|
10
|
-
return Number.isInteger(number)&&number>=minimum
|
|
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
|
|
39
|
-
|
|
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,' ')
|
|
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=
|
|
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=
|
|
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
|
|
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=
|
|
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
|
|
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))
|
|
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
|
-
|
|
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=
|
|
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=
|
|
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')
|
|
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
|
|
56
|
+
return {...result,score,level:levelForScore(score),matches:[...result.matches,blockedDomainSignal]};
|
|
56
57
|
}
|
|
57
58
|
|
|
58
59
|
export function scamSafetyGuidance(result){
|
package/src/import-map.mjs
CHANGED
|
@@ -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:
|
|
867
|
+
excludedModules:[]
|
|
874
868
|
};
|
|
875
869
|
}
|
|
876
870
|
|