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.
- package/CHANGELOG.md +27 -0
- package/README.md +86 -117
- package/bin/arcane-test.mjs +170 -46
- package/browser-runtime/ai/browser-speech-artifacts.mjs +887 -909
- package/browser-runtime/ai/browser-speech-providers.mjs +96 -152
- package/browser-runtime/ai/browser-wasm-llm-provider.mjs +627 -819
- package/browser-runtime/ai/browser-wasm.mjs +24 -35
- package/browser-runtime/ai/browser-wllama-runtime.mjs +64 -316
- package/browser-runtime/ai/model-controller.mjs +584 -181
- package/browser-runtime/ai/speech-worker-client.mjs +8 -146
- package/browser-runtime/ai/speech-worker-runtime.mjs +643 -363
- package/browser-runtime/dom-event-instrumentation.mjs +55 -147
- package/browser-runtime/event-manager.mjs +239 -624
- package/package.json +5 -6
- package/runtime/arcane/components/app-bar.html +3 -15
- package/runtime/arcane/components/assistant-panel.html +10 -10
- package/runtime/arcane/components/calculator.html +1 -1
- package/runtime/arcane/components/chat.html +1359 -135
- package/runtime/arcane/components/conversation-view.html +2 -2
- package/runtime/arcane/components/document-inspector.html +11 -17
- package/runtime/arcane/components/file-manager.html +13 -56
- package/runtime/arcane/components/markdown-document.html +82 -281
- package/runtime/arcane/components/markdown-editor.html +7 -10
- package/runtime/arcane/components/media-embed.html +6 -6
- package/runtime/arcane/components/screen-capture.html +4 -4
- package/runtime/arcane/components/source-explanation.html +2 -2
- package/runtime/arcane/components/speech.html +112 -68
- package/runtime/arcane/components/terminal-workspace.html +4 -4
- package/runtime/arcane/components/theme-editor.html +1 -1
- package/runtime/arcane/components/unified-inbox.html +2 -2
- package/runtime/arcane/components/voice-transcription.html +31 -21
- package/runtime/arcane/entities/Calculation.js +2 -3
- package/runtime/arcane/entities/Chat.js +228 -43
- package/runtime/arcane/entities/Preference.js +3 -5
- package/runtime/arcane/entities/Weather.js +5 -5
- package/runtime/arcane/modules/AI.js +1050 -427
- package/runtime/arcane/modules/AIProviderRuntime.js +658 -363
- package/runtime/arcane/modules/AIResponseLength.js +9 -19
- package/runtime/arcane/modules/AIRuntimeState.js +109 -72
- package/runtime/arcane/modules/ArcaneNavigationPolicy.js +45 -32
- package/runtime/arcane/modules/BrowserTestSuite.js +78 -122
- package/runtime/arcane/modules/CalculatorEngine.js +9 -9
- package/runtime/arcane/modules/CommunicationAppController.js +3 -7
- package/runtime/arcane/modules/ComponentContracts.js +30 -32
- package/runtime/arcane/modules/ConfiguredAIChatSession.js +281 -230
- package/runtime/arcane/modules/ConversationActionItems.js +26 -59
- package/runtime/arcane/modules/ConversationClosingReport.js +34 -61
- package/runtime/arcane/modules/ConversationTimebox.js +27 -15
- package/runtime/arcane/modules/DBOPFSDocumentLibrary.js +152 -344
- package/runtime/arcane/modules/DocumentLexicalSearch.js +25 -91
- package/runtime/arcane/modules/HTMLImport.js +54 -1
- package/runtime/arcane/modules/IsolatedModelQuestionRunner.js +40 -203
- package/runtime/arcane/modules/LocalAIReadiness.js +40 -60
- package/runtime/arcane/modules/LocalAIReadinessController.js +15 -13
- package/runtime/arcane/modules/MD.js +1 -45
- package/runtime/arcane/modules/Mail.js +51 -103
- package/runtime/arcane/modules/MailOutbox.mjs +95 -193
- package/runtime/arcane/modules/MailTransport.mjs +36 -57
- package/runtime/arcane/modules/ModelDefinition.js +22 -106
- package/runtime/arcane/modules/OpenMeteoWeatherProvider.js +39 -101
- package/runtime/arcane/modules/PersistentAIChatSession.js +281 -18
- package/runtime/arcane/modules/PreferenceStore.js +102 -30
- package/runtime/arcane/modules/RiskSignalAnalyzer.js +8 -9
- package/runtime/arcane/modules/ScopedOPFSCache.js +7 -42
- package/runtime/arcane/modules/ScreenCapture.js +175 -128
- package/runtime/arcane/modules/SpeechPlayback.js +46 -149
- package/runtime/arcane/modules/StaticDocumentCatalog.js +173 -407
- package/runtime/arcane/modules/ToolCallRouter.js +25 -12
- package/runtime/arcane/modules/YouTubeMedia.js +6 -5
- package/schemas/arcane-app-bundle.schema.json +13 -78
- package/schemas/arcane-app.schema.json +9 -25
- package/schemas/arcane-lock.schema.json +18 -151
- package/schemas/arcane-package.schema.json +2 -16
- package/schemas/native-build-plan.schema.json +119 -122
- package/src/app-descriptor.mjs +75 -132
- package/src/application-tests.mjs +200 -0
- package/src/cli/main.mjs +27 -46
- package/src/constants.mjs +3 -4
- package/src/dev-server.mjs +30 -324
- package/src/doctor.mjs +92 -154
- package/src/dom-event-instrumentation.mjs +55 -147
- package/src/errors.mjs +2 -3
- package/src/event-manager.mjs +239 -624
- package/src/event-queue.mjs +3 -3
- package/src/import-map.mjs +273 -1028
- package/src/index.mjs +14 -16
- package/src/installed-sdk-runtime.mjs +40 -62
- package/src/integrated-provider-loader.mjs +53 -382
- package/src/mail-api.mjs +0 -2
- package/src/mail-server.mjs +224 -580
- package/src/mail.mjs +4 -10
- package/src/native-plan.mjs +163 -598
- package/src/native-provider-loader.mjs +104 -1063
- package/src/packager/core.mjs +485 -3229
- package/src/process.mjs +5 -10
- package/src/release-bundle.mjs +292 -2405
- package/src/runtime.mjs +76 -396
- package/src/scaffold.mjs +30 -80
- package/src/sdk-browser-runtime.mjs +70 -626
- package/src/source-server.mjs +588 -0
- package/src/targets/index.mjs +78 -188
- package/src/templates/workspace-template.mjs +19 -135
- package/src/testing-loader.mjs +164 -0
- package/src/testing.mjs +1 -1
- package/src/toolchain.mjs +131 -544
- package/src/update-check.mjs +26 -64
- package/src/workspace-operation-lock.mjs +139 -430
- package/src/workspace-runtime.mjs +112 -779
- package/src/workspace.mjs +40 -302
- package/browser-runtime/ARCANE_SDK_BROWSER_RELEASE.json +0 -218
- package/browser-runtime/ai/ARCANE_AI_BROWSER_SPEECH_COMPONENTS.json +0 -203
- package/browser-runtime/ai/ARCANE_AI_BROWSER_WASM_COMPONENTS.json +0 -80
- package/browser-runtime/ai/internal/sha256.mjs +0 -166
- package/docs/architecture.md +0 -344
- package/docs/compatibility.md +0 -36
- package/docs/event-manager.md +0 -294
- package/docs/platform-targets.md +0 -108
- package/docs/publishing.md +0 -201
- package/docs/reference/README.md +0 -187
- package/docs/reference/ai/browser-speech-package-authority.json +0 -835
- package/docs/reference/ai/browser-speech.md +0 -1252
- package/docs/reference/ai/browser-wasm.md +0 -530
- package/docs/reference/arcane-ollama.md +0 -288
- package/docs/reference/availability-and-normalization.md +0 -183
- package/docs/reference/behavioral-testing.md +0 -133
- package/docs/reference/cli.md +0 -779
- package/docs/reference/core/README.md +0 -62
- package/docs/reference/core/arcane-ai-contracts.md +0 -907
- package/docs/reference/core/arcane-api.md +0 -601
- package/docs/reference/core/arcane-entities.md +0 -65
- package/docs/reference/core/arcane-events.md +0 -134
- package/docs/reference/core/ollama-module.md +0 -181
- package/docs/reference/core/reference/arcane-api/ai-and-ollama.md +0 -1909
- package/docs/reference/core/reference/arcane-api/applications-terminal-capabilities.md +0 -1057
- package/docs/reference/core/reference/arcane-api/core-and-events.md +0 -320
- package/docs/reference/core/reference/arcane-api/filesystem-storage-preferences-appearance.md +0 -610
- package/docs/reference/core/reference/arcane-api/namespaces.md +0 -1157
- package/docs/reference/core/reference/arcane-api/platform-installation-users-system.md +0 -1423
- package/docs/reference/core/reference/arcane-api/session-provisioning-diagnostics-development.md +0 -315
- package/docs/reference/event-manager.md +0 -1511
- package/docs/reference/inventory/package-api.json +0 -3284
- package/docs/reference/inventory/runtime-components.json +0 -1011
- package/docs/reference/inventory/runtime-entities.json +0 -26
- package/docs/reference/inventory/runtime-modules.json +0 -1431
- package/docs/reference/mail.md +0 -316
- package/docs/reference/protocols.md +0 -677
- package/docs/reference/runtime-components.md +0 -1366
- package/docs/reference/runtime-entities.md +0 -303
- package/docs/reference/runtime-modules.md +0 -2960
- package/docs/reference/sdk-api.md +0 -6694
- package/docs/roadmap.md +0 -79
- package/docs/work-amplification.md +0 -129
- package/runtime/ARCANE_RUNTIME_RELEASE.json +0 -826
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
const DOCUMENT_SEARCH_FIELD_ORDER=
|
|
1
|
+
const DOCUMENT_SEARCH_FIELD_ORDER=[
|
|
2
2
|
'title','searchTerms','tags','headings','summary','category','navigationGroup',
|
|
3
3
|
'navigationParent','audiences','platforms','sourcePath','path','language','id'
|
|
4
|
-
]
|
|
4
|
+
];
|
|
5
5
|
const SEARCH_STOP_WORDS=new Set([
|
|
6
6
|
'a','an','and','are','as','at','be','by','do','does','for','from','how','i',
|
|
7
7
|
'in','is','it','of','on','or','that','the','this','to','use','using','what',
|
|
@@ -28,8 +28,7 @@ function normalizedDocumentSearchText(value){
|
|
|
28
28
|
|
|
29
29
|
function documentSearchTokens(value){
|
|
30
30
|
return [...new Set(normalizedDocumentSearchText(value).match(/[\p{L}\p{N}]+/gu)??[])]
|
|
31
|
-
.filter(token=>!SEARCH_STOP_WORDS.has(token))
|
|
32
|
-
.slice(0,32);
|
|
31
|
+
.filter(token=>!SEARCH_STOP_WORDS.has(token));
|
|
33
32
|
}
|
|
34
33
|
|
|
35
34
|
function list(value,mapper=normalizedDocumentSearchText){
|
|
@@ -38,7 +37,7 @@ function list(value,mapper=normalizedDocumentSearchText){
|
|
|
38
37
|
|
|
39
38
|
function createDocumentLexicalIndex(record){
|
|
40
39
|
if(!isPlainRecord(record)) fail('Document search records must be plain objects.');
|
|
41
|
-
return
|
|
40
|
+
return {
|
|
42
41
|
audiences:list(record.audiences),
|
|
43
42
|
category:normalizedDocumentSearchText(record.category),
|
|
44
43
|
headings:list(record.headings,heading=>normalizedDocumentSearchText(heading?.text)),
|
|
@@ -53,7 +52,7 @@ function createDocumentLexicalIndex(record){
|
|
|
53
52
|
summary:normalizedDocumentSearchText(record.summary),
|
|
54
53
|
tags:list(record.tags),
|
|
55
54
|
title:normalizedDocumentSearchText(record.title),
|
|
56
|
-
}
|
|
55
|
+
};
|
|
57
56
|
}
|
|
58
57
|
|
|
59
58
|
function scoreDocumentLexicalIndex(index,phrase,tokens){
|
|
@@ -104,7 +103,7 @@ function scoreDocumentLexicalIndex(index,phrase,tokens){
|
|
|
104
103
|
if(index.path.includes(token)){score+=4;matched.add('path');}
|
|
105
104
|
if(index.id.includes(token)){score+=5;matched.add('id');}
|
|
106
105
|
}
|
|
107
|
-
return
|
|
106
|
+
return {matched,score};
|
|
108
107
|
}
|
|
109
108
|
|
|
110
109
|
function scoreDocumentBody(value,phrase,tokens){
|
|
@@ -125,77 +124,26 @@ function compareText(left,right){
|
|
|
125
124
|
return left<right?-1:left>right?1:0;
|
|
126
125
|
}
|
|
127
126
|
|
|
128
|
-
function boundedSearchResults(results,limit){
|
|
129
|
-
if(results.length<=limit) return results;
|
|
130
|
-
const collectionLimit=Math.max(1,Math.floor(limit/4));
|
|
131
|
-
const collectionCounts=new Map();
|
|
132
|
-
const selected=new Set();
|
|
133
|
-
const deferred=[];
|
|
134
|
-
for(const result of results){
|
|
135
|
-
if(selected.size>=limit) break;
|
|
136
|
-
if(!result.navigationParent){selected.add(result);continue;}
|
|
137
|
-
const parent=canonicalKey(result.navigationParent);
|
|
138
|
-
const count=collectionCounts.get(parent)??0;
|
|
139
|
-
if(count>=collectionLimit){deferred.push(result);continue;}
|
|
140
|
-
collectionCounts.set(parent,count+1);
|
|
141
|
-
selected.add(result);
|
|
142
|
-
}
|
|
143
|
-
for(const result of deferred){
|
|
144
|
-
if(selected.size>=limit) break;
|
|
145
|
-
selected.add(result);
|
|
146
|
-
}
|
|
147
|
-
return results.filter(result=>selected.has(result));
|
|
148
|
-
}
|
|
149
|
-
|
|
150
127
|
function normalizeQuery(value){
|
|
151
128
|
if(typeof value!=='string') fail('Search query must be a string.','DOCUMENT_SEARCH_INVALID_QUERY');
|
|
152
129
|
const query=value.trim();
|
|
153
|
-
if(
|
|
154
|
-
fail('Search query must be
|
|
130
|
+
if(CONTROL_CHARACTERS.test(query)){
|
|
131
|
+
fail('Search query must be plain text.','DOCUMENT_SEARCH_INVALID_QUERY');
|
|
155
132
|
}
|
|
156
133
|
return query;
|
|
157
134
|
}
|
|
158
135
|
|
|
159
136
|
function normalizeFilter(value,label){
|
|
160
137
|
if(value===undefined) return null;
|
|
161
|
-
if(!Array.isArray(value)
|
|
138
|
+
if(!Array.isArray(value)) fail(`${label} must be an array.`,'DOCUMENT_SEARCH_INVALID_QUERY');
|
|
162
139
|
return new Set(value.map((item,index)=>{
|
|
163
|
-
if(typeof item!=='string'||!item.trim()
|
|
164
|
-
fail(`${label} entry ${index+1} must
|
|
140
|
+
if(typeof item!=='string'||!item.trim()){
|
|
141
|
+
fail(`${label} entry ${index+1} must contain text.`,'DOCUMENT_SEARCH_INVALID_QUERY');
|
|
165
142
|
}
|
|
166
143
|
return canonicalKey(item.trim());
|
|
167
144
|
}));
|
|
168
145
|
}
|
|
169
146
|
|
|
170
|
-
function safeSlice(value,maximum){
|
|
171
|
-
if(value.length<=maximum) return value;
|
|
172
|
-
let end=maximum;
|
|
173
|
-
const code=value.charCodeAt(end-1);
|
|
174
|
-
if(code>=0xd800&&code<=0xdbff) end--;
|
|
175
|
-
return value.slice(0,end);
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
function relevantSliceStart(value,query,maximum){
|
|
179
|
-
if(value.length<=maximum) return 0;
|
|
180
|
-
const phrase=String(query||'').trim().toLowerCase();
|
|
181
|
-
const tokens=documentSearchTokens(query);
|
|
182
|
-
const body=value.toLowerCase();
|
|
183
|
-
const positions=[phrase,...tokens]
|
|
184
|
-
.filter(Boolean)
|
|
185
|
-
.map(term=>body.indexOf(term))
|
|
186
|
-
.filter(index=>index>=0);
|
|
187
|
-
const match=positions.length?Math.min(...positions):0;
|
|
188
|
-
let start=Math.max(0,match-Math.floor(maximum/3));
|
|
189
|
-
const priorNewline=start>0?value.lastIndexOf('\n',start-1):-1;
|
|
190
|
-
const alignedStart=priorNewline+1;
|
|
191
|
-
if(match-alignedStart<=Math.floor(maximum*2/3)) start=alignedStart;
|
|
192
|
-
if(start>0){
|
|
193
|
-
const code=value.charCodeAt(start);
|
|
194
|
-
if(code>=0xdc00&&code<=0xdfff) start++;
|
|
195
|
-
}
|
|
196
|
-
return start;
|
|
197
|
-
}
|
|
198
|
-
|
|
199
147
|
function lineNumberAt(value,offset){
|
|
200
148
|
let line=1;
|
|
201
149
|
let cursor=value.indexOf('\n');
|
|
@@ -203,33 +151,23 @@ function lineNumberAt(value,offset){
|
|
|
203
151
|
return line;
|
|
204
152
|
}
|
|
205
153
|
|
|
206
|
-
function documentContextExcerpt(value
|
|
154
|
+
function documentContextExcerpt(value){
|
|
207
155
|
if(typeof value!=='string') fail('Document context must be text.');
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
lineEnd:lineNumberAt(value,Math.max(start,end-1)),
|
|
214
|
-
lineStart:lineNumberAt(value,start),
|
|
215
|
-
text,
|
|
216
|
-
truncated:start>0||end<value.length,
|
|
217
|
-
});
|
|
156
|
+
return {
|
|
157
|
+
lineEnd:lineNumberAt(value,Math.max(0,value.length-1)),
|
|
158
|
+
lineStart:1,
|
|
159
|
+
text:value,
|
|
160
|
+
};
|
|
218
161
|
}
|
|
219
162
|
|
|
220
163
|
class DocumentLexicalSearch{
|
|
221
164
|
#indexes;
|
|
222
|
-
#maxResults;
|
|
223
165
|
#records;
|
|
224
166
|
|
|
225
|
-
constructor(records
|
|
167
|
+
constructor(records){
|
|
226
168
|
if(!Array.isArray(records)) fail('Document search records must be an array.');
|
|
227
|
-
|
|
228
|
-
fail('maxResults must be an integer from 1 through 100.');
|
|
229
|
-
}
|
|
230
|
-
this.#records=Object.freeze([...records]);
|
|
169
|
+
this.#records=[...records];
|
|
231
170
|
this.#indexes=new Map(this.#records.map(record=>[record.id,createDocumentLexicalIndex(record)]));
|
|
232
|
-
this.#maxResults=maxResults;
|
|
233
171
|
}
|
|
234
172
|
|
|
235
173
|
rank(query,options={}){
|
|
@@ -249,32 +187,28 @@ class DocumentLexicalSearch{
|
|
|
249
187
|
?scoreDocumentLexicalIndex(this.#indexes.get(record.id),phrase,tokens)
|
|
250
188
|
:{matched:new Set(),score:0};
|
|
251
189
|
if(text&&!score) continue;
|
|
252
|
-
results.push(
|
|
190
|
+
results.push({
|
|
253
191
|
...record,
|
|
254
|
-
matchedFields:
|
|
192
|
+
matchedFields:DOCUMENT_SEARCH_FIELD_ORDER.filter(field=>matched.has(field)),
|
|
255
193
|
score,
|
|
256
|
-
})
|
|
194
|
+
});
|
|
257
195
|
}
|
|
258
196
|
results.sort((left,right)=>
|
|
259
197
|
right.score-left.score
|
|
260
198
|
||compareText(normalizedDocumentSearchText(left.title),normalizedDocumentSearchText(right.title))
|
|
261
199
|
||compareText(String(left.id),String(right.id))
|
|
262
200
|
);
|
|
263
|
-
return
|
|
201
|
+
return results;
|
|
264
202
|
}
|
|
265
203
|
|
|
266
204
|
search(query,options={}){
|
|
267
205
|
if(!isPlainRecord(options)) fail('Search options must be a plain object.','DOCUMENT_SEARCH_INVALID_QUERY');
|
|
268
206
|
const unknown=Object.keys(options).find(key=>!['kinds','limit','tags'].includes(key));
|
|
269
207
|
if(unknown) fail(`Search options contain an unsupported field: ${unknown}.`,'DOCUMENT_SEARCH_INVALID_QUERY');
|
|
270
|
-
|
|
271
|
-
if(!Number.isSafeInteger(limit)||limit<1||limit>this.#maxResults){
|
|
272
|
-
fail(`Search result limit must be an integer from 1 through ${this.#maxResults}.`,'DOCUMENT_SEARCH_INVALID_QUERY');
|
|
273
|
-
}
|
|
274
|
-
return Object.freeze(boundedSearchResults(this.rank(query,{
|
|
208
|
+
return this.rank(query,{
|
|
275
209
|
kinds:options.kinds,
|
|
276
210
|
tags:options.tags,
|
|
277
|
-
})
|
|
211
|
+
});
|
|
278
212
|
}
|
|
279
213
|
}
|
|
280
214
|
|
|
@@ -12,6 +12,59 @@ globalThis[htmlImportHostRegistryKey]=htmlImportHostRegistry;
|
|
|
12
12
|
|
|
13
13
|
let htmlImportScriptId=0;
|
|
14
14
|
|
|
15
|
+
function componentRuntimeRoot(resolvedHref){
|
|
16
|
+
const componentURL=new URL(resolvedHref);
|
|
17
|
+
const componentMarker='/arcane/components/';
|
|
18
|
+
const componentIndex=componentURL.pathname.lastIndexOf(componentMarker);
|
|
19
|
+
if(componentIndex<0)return null;
|
|
20
|
+
const runtimePath=componentURL.pathname.slice(
|
|
21
|
+
0,
|
|
22
|
+
componentIndex+'/arcane/'.length
|
|
23
|
+
);
|
|
24
|
+
return new URL(runtimePath,componentURL.origin);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function resolveComponentResource(value,runtimeRoot){
|
|
28
|
+
if(!runtimeRoot||typeof value!=='string'||!value.startsWith('./arcane/')){
|
|
29
|
+
return value;
|
|
30
|
+
}
|
|
31
|
+
return new URL(value.slice('./arcane/'.length),runtimeRoot).href;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function resolveComponentStyleResources(styleText,runtimeRoot){
|
|
35
|
+
if(!runtimeRoot)return styleText;
|
|
36
|
+
return styleText.replace(
|
|
37
|
+
/url\(\s*(['"]?)\.\/arcane\/([^)'"\s]+)\1\s*\)/gu,
|
|
38
|
+
function resolveStyleURL(_match,quote,resourcePath){
|
|
39
|
+
const resolved=new URL(resourcePath,runtimeRoot).href;
|
|
40
|
+
return `url(${quote}${resolved}${quote})`;
|
|
41
|
+
}
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function createComponentFragment(html,resolvedHref){
|
|
46
|
+
const template=document.createElement('template');
|
|
47
|
+
template.innerHTML=html;
|
|
48
|
+
const runtimeRoot=componentRuntimeRoot(resolvedHref);
|
|
49
|
+
if(!runtimeRoot)return template.content;
|
|
50
|
+
|
|
51
|
+
for(const element of template.content.querySelectorAll('[href],[src]')){
|
|
52
|
+
for(const attribute of ['href','src']){
|
|
53
|
+
if(!element.hasAttribute(attribute))continue;
|
|
54
|
+
const value=element.getAttribute(attribute);
|
|
55
|
+
const resolved=resolveComponentResource(value,runtimeRoot);
|
|
56
|
+
if(resolved!==value)element.setAttribute(attribute,resolved);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
for(const style of template.content.querySelectorAll('style')){
|
|
60
|
+
style.textContent=resolveComponentStyleResources(
|
|
61
|
+
style.textContent||'',
|
|
62
|
+
runtimeRoot
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
return template.content;
|
|
66
|
+
}
|
|
67
|
+
|
|
15
68
|
function samePropertyDescriptor(left,right){
|
|
16
69
|
if(!left||!right)return left===right;
|
|
17
70
|
return left.configurable===right.configurable
|
|
@@ -219,7 +272,7 @@ class HTMLImport extends HTMLElement {
|
|
|
219
272
|
if(!this.#isCurrentConnection(generation,controller))return false;
|
|
220
273
|
await this.#destroyImportedHost();
|
|
221
274
|
if(!this.#isCurrentConnection(generation,controller))return false;
|
|
222
|
-
this.shadowRoot.
|
|
275
|
+
this.shadowRoot.replaceChildren(createComponentFragment(html,resolvedHref));
|
|
223
276
|
|
|
224
277
|
await this.#executeScripts();
|
|
225
278
|
if(!this.#isCurrentConnection(generation,controller)){
|
|
@@ -1,22 +1,5 @@
|
|
|
1
|
-
const
|
|
2
|
-
|
|
3
|
-
'name',
|
|
4
|
-
'provider',
|
|
5
|
-
'digest',
|
|
6
|
-
'sizeBytes',
|
|
7
|
-
'modifiedAt'
|
|
8
|
-
]);
|
|
9
|
-
const RUN_REQUEST_KEYS=Object.freeze([
|
|
10
|
-
'model',
|
|
11
|
-
'prompt',
|
|
12
|
-
'systemPrompt',
|
|
13
|
-
'options',
|
|
14
|
-
'expectedModel'
|
|
15
|
-
]);
|
|
16
|
-
const RUN_REQUEST_OPTIONAL_KEYS=Object.freeze(['onPhase','think']);
|
|
17
|
-
const THINK_LEVELS=Object.freeze(['low','medium','high']);
|
|
18
|
-
const SENTENCE_BOUNDARY=/[.!?]+(?:["'\u2019\u201d)\]}]+)?(?=\s|$)/gu;
|
|
19
|
-
const WORD_OR_NUMBER=/[\p{L}\p{N}]/u;
|
|
1
|
+
const SENTENCE_BOUNDARY=/[.!?]+(?:["'\\u2019\\u201d)\\]}]+)?(?=\\s|$)/gu;
|
|
2
|
+
const WORD_OR_NUMBER=/[\\p{L}\\p{N}]/u;
|
|
20
3
|
|
|
21
4
|
function codedError(code,message,ErrorType=Error){
|
|
22
5
|
const error=new ErrorType(message);
|
|
@@ -27,162 +10,25 @@ function codedError(code,message,ErrorType=Error){
|
|
|
27
10
|
function isPlainRecord(value){
|
|
28
11
|
return Boolean(value)
|
|
29
12
|
&&typeof value==='object'
|
|
30
|
-
&&!Array.isArray(value)
|
|
31
|
-
&&Object.getPrototypeOf(value)===Object.prototype;
|
|
13
|
+
&&!Array.isArray(value);
|
|
32
14
|
}
|
|
33
15
|
|
|
34
|
-
function hasExactKeys(value,required,optional=[]){
|
|
35
|
-
if(!isPlainRecord(value)){
|
|
36
|
-
return false;
|
|
37
|
-
}
|
|
38
|
-
const requiredSet=new Set(required);
|
|
39
|
-
const allowed=new Set([...required,...optional]);
|
|
40
|
-
const keys=Object.keys(value);
|
|
41
|
-
return required.every(function requiredKey(key){return Object.hasOwn(value,key);})
|
|
42
|
-
&&keys.every(function allowedKey(key){return allowed.has(key);})
|
|
43
|
-
&&keys.filter(function requiredKey(key){return requiredSet.has(key);}).length===required.length;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
function exactTimestamp(value){
|
|
47
|
-
if(typeof value!=='string'){
|
|
48
|
-
return null;
|
|
49
|
-
}
|
|
50
|
-
const milliseconds=Date.parse(value);
|
|
51
|
-
if(!Number.isFinite(milliseconds)||new Date(milliseconds).toISOString()!==value){
|
|
52
|
-
return null;
|
|
53
|
-
}
|
|
54
|
-
return milliseconds;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
function validModifiedAt(value){
|
|
58
|
-
return value===null||(typeof value==='string'&&Number.isFinite(Date.parse(value)));
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
function validateExpectedModel(model,expectedModel){
|
|
62
|
-
const valid=typeof model==='string'
|
|
63
|
-
&&model.length>0
|
|
64
|
-
&&model===model.trim()
|
|
65
|
-
&&hasExactKeys(expectedModel,MODEL_EVIDENCE_KEYS)
|
|
66
|
-
&&expectedModel.id===model
|
|
67
|
-
&&expectedModel.name===model
|
|
68
|
-
&&expectedModel.provider==='ollama'
|
|
69
|
-
&&/^[a-f0-9]{64}$/i.test(expectedModel.digest)
|
|
70
|
-
&&Number.isSafeInteger(expectedModel.sizeBytes)
|
|
71
|
-
&&expectedModel.sizeBytes>0
|
|
72
|
-
&&validModifiedAt(expectedModel.modifiedAt);
|
|
73
|
-
if(!valid){
|
|
74
|
-
throw codedError(
|
|
75
|
-
'INVALID_ISOLATED_MODEL_RUNNER_REQUEST',
|
|
76
|
-
'The isolated-model request requires exact authoritative model evidence.',
|
|
77
|
-
TypeError
|
|
78
|
-
);
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
function sameModelEvidence(actual,expected){
|
|
83
|
-
return hasExactKeys(actual,MODEL_EVIDENCE_KEYS)
|
|
84
|
-
&&MODEL_EVIDENCE_KEYS.every(function matchingField(field){
|
|
85
|
-
return actual[field]===expected[field];
|
|
86
|
-
});
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
function validDefaults(value){
|
|
90
|
-
return hasExactKeys(value,['systemPromptPresent','messageCount'])
|
|
91
|
-
&&value.systemPromptPresent===false
|
|
92
|
-
&&value.messageCount===0;
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
function invalidProof(message){
|
|
96
|
-
throw codedError(
|
|
97
|
-
'ARCANE_ISOLATED_MODEL_PROOF_INVALID',
|
|
98
|
-
message||'Arcane Core returned an invalid isolated-model proof.'
|
|
99
|
-
);
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
function validateInspection(result,expectedModel,contextTokens){
|
|
103
|
-
if(!hasExactKeys(result,['schemaVersion','model','defaults','admission'])
|
|
104
|
-
||result.schemaVersion!==1
|
|
105
|
-
||!sameModelEvidence(result.model,expectedModel)
|
|
106
|
-
||!validDefaults(result.defaults)
|
|
107
|
-
||!isPlainRecord(result.admission)
|
|
108
|
-
||result.admission.admitted!==true
|
|
109
|
-
||result.admission.contextTokens!==contextTokens){
|
|
110
|
-
invalidProof('Arcane Core returned an invalid isolated-model inspection proof.');
|
|
111
|
-
}
|
|
112
|
-
return result;
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
function validateAbsenceProof(value){
|
|
116
|
-
return hasExactKeys(value,['absent','observedAt','polls'])
|
|
117
|
-
&&value.absent===true
|
|
118
|
-
&&exactTimestamp(value.observedAt)!==null
|
|
119
|
-
&&Number.isSafeInteger(value.polls)
|
|
120
|
-
&&value.polls>0;
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
function validateIsolation(value){
|
|
124
|
-
if(!hasExactKeys(value,['pre','post','keepAlive','messageCount','defaults'])
|
|
125
|
-
||!validateAbsenceProof(value.pre)
|
|
126
|
-
||!validateAbsenceProof(value.post)
|
|
127
|
-
||exactTimestamp(value.pre.observedAt)>exactTimestamp(value.post.observedAt)
|
|
128
|
-
||value.keepAlive!==0
|
|
129
|
-
||value.messageCount!==2
|
|
130
|
-
||!validDefaults(value.defaults)){
|
|
131
|
-
invalidProof('Arcane Core did not prove the required isolated-model lifecycle.');
|
|
132
|
-
}
|
|
133
|
-
return value;
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
function validateRunResult(result,expectedModel){
|
|
137
|
-
if(!hasExactKeys(result,[
|
|
138
|
-
'schemaVersion',
|
|
139
|
-
'model',
|
|
140
|
-
'answer',
|
|
141
|
-
'startedAt',
|
|
142
|
-
'completedAt',
|
|
143
|
-
'elapsedMs',
|
|
144
|
-
'isolation'
|
|
145
|
-
])
|
|
146
|
-
||result.schemaVersion!==1
|
|
147
|
-
||!sameModelEvidence(result.model,expectedModel)
|
|
148
|
-
||typeof result.answer!=='string'){
|
|
149
|
-
invalidProof('Arcane Core returned an invalid isolated-model response.');
|
|
150
|
-
}
|
|
151
|
-
const started=exactTimestamp(result.startedAt);
|
|
152
|
-
const completed=exactTimestamp(result.completedAt);
|
|
153
|
-
if(started===null
|
|
154
|
-
||completed===null
|
|
155
|
-
||completed<started
|
|
156
|
-
||!Number.isFinite(result.elapsedMs)
|
|
157
|
-
||result.elapsedMs<0){
|
|
158
|
-
invalidProof('Arcane Core returned invalid isolated-model timing evidence.');
|
|
159
|
-
}
|
|
160
|
-
validateIsolation(result.isolation);
|
|
161
|
-
return result;
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
/**
|
|
165
|
-
* Counts terminal sentence-punctuation groups and one final unpunctuated
|
|
166
|
-
* fragment. The heuristic is deliberately deterministic and never rewrites
|
|
167
|
-
* the response it observes.
|
|
168
|
-
*/
|
|
169
16
|
function countSentences(value){
|
|
170
17
|
if(typeof value!=='string'){
|
|
171
18
|
throw new TypeError('Sentence counting requires a string.');
|
|
172
19
|
}
|
|
173
|
-
|
|
174
|
-
if(!text){
|
|
20
|
+
if(!value.trim()){
|
|
175
21
|
return 0;
|
|
176
22
|
}
|
|
177
23
|
let count=0;
|
|
178
24
|
let consumed=0;
|
|
179
25
|
SENTENCE_BOUNDARY.lastIndex=0;
|
|
180
|
-
for(const match of
|
|
26
|
+
for(const match of value.matchAll(SENTENCE_BOUNDARY)){
|
|
181
27
|
count+=1;
|
|
182
28
|
consumed=Number(match.index)+match[0].length;
|
|
183
29
|
}
|
|
184
|
-
const trailing=
|
|
185
|
-
if(trailing&&(count===0||WORD_OR_NUMBER.test(trailing))){
|
|
30
|
+
const trailing=value.slice(consumed);
|
|
31
|
+
if(trailing.trim()&&(count===0||WORD_OR_NUMBER.test(trailing))){
|
|
186
32
|
count+=1;
|
|
187
33
|
}
|
|
188
34
|
return count;
|
|
@@ -201,42 +47,45 @@ function requireLocalAI(localAI){
|
|
|
201
47
|
}
|
|
202
48
|
|
|
203
49
|
class IsolatedModelQuestionRunner{
|
|
204
|
-
constructor({localAI
|
|
205
|
-
if(!Number.isSafeInteger(maxSentences)||maxSentences<1||maxSentences>100){
|
|
206
|
-
throw new RangeError('maxSentences must be an integer from 1 through 100.');
|
|
207
|
-
}
|
|
50
|
+
constructor({localAI}={}){
|
|
208
51
|
this.localAI=requireLocalAI(localAI);
|
|
209
|
-
this.maxSentences=maxSentences;
|
|
210
52
|
}
|
|
211
53
|
|
|
212
54
|
async inspectModel(model,expectedModel,contextTokens){
|
|
213
|
-
|
|
214
|
-
if(!Number.isSafeInteger(contextTokens)||contextTokens<1024||contextTokens>262144){
|
|
55
|
+
if(typeof model!=='string'||!model.trim()){
|
|
215
56
|
throw codedError(
|
|
216
57
|
'INVALID_ISOLATED_MODEL_RUNNER_REQUEST',
|
|
217
|
-
'The isolated-model inspection requires a
|
|
58
|
+
'The isolated-model inspection requires a model.',
|
|
59
|
+
TypeError
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
if(contextTokens!==undefined&&(!Number.isSafeInteger(contextTokens)||contextTokens<1)){
|
|
63
|
+
throw codedError(
|
|
64
|
+
'INVALID_ISOLATED_MODEL_RUNNER_REQUEST',
|
|
65
|
+
'The isolated-model inspection context token value must be positive when provided.',
|
|
218
66
|
RangeError
|
|
219
67
|
);
|
|
220
68
|
}
|
|
221
|
-
const
|
|
222
|
-
|
|
69
|
+
const request={model};
|
|
70
|
+
if(expectedModel!==undefined)request.expectedModel=expectedModel;
|
|
71
|
+
if(contextTokens!==undefined)request.contextTokens=contextTokens;
|
|
72
|
+
return this.localAI.inspectIsolatedModel(request);
|
|
223
73
|
}
|
|
224
74
|
|
|
225
75
|
async runQuestion(input={}){
|
|
226
|
-
if(!
|
|
76
|
+
if(!isPlainRecord(input)){
|
|
227
77
|
throw codedError(
|
|
228
78
|
'INVALID_ISOLATED_MODEL_RUNNER_REQUEST',
|
|
229
|
-
'The isolated-model question request
|
|
79
|
+
'The isolated-model question request must be an object.',
|
|
230
80
|
TypeError
|
|
231
81
|
);
|
|
232
82
|
}
|
|
233
|
-
const {
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
||typeof systemPrompt!=='string'
|
|
238
|
-
|
|
239
|
-
||(hasThink&&!THINK_LEVELS.includes(think))
|
|
83
|
+
const {onPhase,...request}=input;
|
|
84
|
+
if(typeof request.model!=='string'
|
|
85
|
+
||!request.model.trim()
|
|
86
|
+
||typeof request.prompt!=='string'
|
|
87
|
+
||(Object.hasOwn(request,'systemPrompt')&&typeof request.systemPrompt!=='string')
|
|
88
|
+
||(Object.hasOwn(request,'options')&&!isPlainRecord(request.options))
|
|
240
89
|
||(onPhase!==undefined&&typeof onPhase!=='function')){
|
|
241
90
|
throw codedError(
|
|
242
91
|
'INVALID_ISOLATED_MODEL_RUNNER_REQUEST',
|
|
@@ -244,30 +93,18 @@ class IsolatedModelQuestionRunner{
|
|
|
244
93
|
TypeError
|
|
245
94
|
);
|
|
246
95
|
}
|
|
247
|
-
const request={
|
|
248
|
-
model,
|
|
249
|
-
prompt,
|
|
250
|
-
systemPrompt,
|
|
251
|
-
options,
|
|
252
|
-
...(hasThink?{think}:{}),
|
|
253
|
-
expectedModel
|
|
254
|
-
};
|
|
255
96
|
const streamOptions=onPhase===undefined?{}:{onPhase};
|
|
256
|
-
const result=
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
model:result.model,
|
|
268
|
-
sentenceCount,
|
|
269
|
-
sentenceLimitExceeded:sentenceCount>this.maxSentences
|
|
270
|
-
});
|
|
97
|
+
const result=await this.localAI.runIsolatedQuestion(request,streamOptions);
|
|
98
|
+
if(!isPlainRecord(result)||typeof result.answer!=='string'){
|
|
99
|
+
throw codedError(
|
|
100
|
+
'ARCANE_ISOLATED_MODEL_RESPONSE_INVALID',
|
|
101
|
+
'Arcane Core returned an invalid isolated-model response.'
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
return {
|
|
105
|
+
...result,
|
|
106
|
+
sentenceCount:countSentences(result.answer)
|
|
107
|
+
};
|
|
271
108
|
}
|
|
272
109
|
}
|
|
273
110
|
|