arcane-os 0.5.17 → 0.5.18
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 +24 -0
- package/README.md +9 -9
- package/bin/arcane-test.mjs +16 -13
- package/browser-runtime/ai/browser-speech-artifacts.mjs +41 -38
- package/browser-runtime/ai/browser-speech-providers.mjs +46 -43
- package/browser-runtime/ai/browser-wasm-llm-provider.mjs +127 -124
- package/browser-runtime/ai/browser-wasm.mjs +6 -3
- package/browser-runtime/ai/browser-wllama-runtime.mjs +26 -23
- package/browser-runtime/ai/model-controller.mjs +94 -91
- package/browser-runtime/ai/speech-worker-client.mjs +12 -9
- package/browser-runtime/ai/speech-worker-runtime.mjs +56 -53
- package/browser-runtime/dependencies/strong-type/index.js +207 -82
- package/browser-runtime/dependencies/strong-type/package.json +15 -6
- package/browser-runtime/dom-event-instrumentation.mjs +24 -20
- package/browser-runtime/event-manager.mjs +111 -108
- package/browser-runtime/speech-text.mjs +5 -1
- package/docs/reference/ai/browser-speech.md +198 -5
- package/docs/reference/availability-and-normalization.md +40 -0
- package/docs/reference/inventory/package-api.json +1 -1
- package/docs/reference/inventory/runtime-components.json +1 -1
- package/docs/reference/inventory/runtime-modules.json +20 -24
- package/docs/reference/protocols.md +1 -1
- package/docs/reference/runtime-modules.md +125 -35
- package/docs/reference/sdk-api.md +23 -7
- package/examples/wasm-ai-demo/index.html +1 -0
- package/node_modules/event-pubsub/node_modules/strong-type/README.md +408 -0
- package/node_modules/event-pubsub/node_modules/strong-type/assets/strong-type-header.png +0 -0
- package/node_modules/event-pubsub/node_modules/strong-type/index.js +1151 -0
- package/node_modules/event-pubsub/node_modules/strong-type/licence +21 -0
- package/node_modules/event-pubsub/node_modules/strong-type/node.js +125 -0
- package/node_modules/event-pubsub/node_modules/strong-type/package.json +61 -0
- package/node_modules/strong-type/README.md +42 -24
- package/node_modules/strong-type/benchmark/core.js +324 -0
- package/node_modules/strong-type/index.js +207 -82
- package/node_modules/strong-type/package.json +14 -6
- package/package.json +3 -3
- package/runtime/arcane/components/app-bar.html +5 -2
- package/runtime/arcane/components/assistant-panel.html +5 -2
- package/runtime/arcane/components/chart.html +15 -12
- package/runtime/arcane/components/chat.html +117 -138
- package/runtime/arcane/components/dashboard-config.html +6 -3
- package/runtime/arcane/components/data-view.html +4 -1
- package/runtime/arcane/components/directory-picker.html +5 -2
- package/runtime/arcane/components/document-inspector.html +6 -3
- package/runtime/arcane/components/file-drop.html +5 -2
- package/runtime/arcane/components/file-inspector.html +9 -6
- package/runtime/arcane/components/file-manager.html +16 -13
- package/runtime/arcane/components/local-ai-status.html +5 -2
- package/runtime/arcane/components/markdown-document.html +14 -11
- package/runtime/arcane/components/markdown-editor.html +4 -1
- package/runtime/arcane/components/modal.html +4 -1
- package/runtime/arcane/components/output-panel.html +6 -3
- package/runtime/arcane/components/preferences-form.html +4 -1
- package/runtime/arcane/components/record-timeline.html +8 -5
- package/runtime/arcane/components/relationship-board.html +9 -6
- package/runtime/arcane/components/source-code-viewer.html +12 -9
- package/runtime/arcane/components/source-explanation.html +8 -5
- package/runtime/arcane/components/speech.html +24 -21
- package/runtime/arcane/components/summary-strip.html +4 -1
- package/runtime/arcane/components/task-progress.html +6 -3
- package/runtime/arcane/components/terminal-workspace.html +4 -1
- package/runtime/arcane/components/voice-transcription.html +11 -8
- package/runtime/arcane/components/web-navigator.html +5 -2
- package/runtime/arcane/entities/ApiModelRecord.js +5 -2
- package/runtime/arcane/entities/Calculation.js +5 -2
- package/runtime/arcane/entities/Chat.js +33 -33
- package/runtime/arcane/entities/CommunicationMessage.js +4 -1
- package/runtime/arcane/entities/CommunicationThread.js +4 -1
- package/runtime/arcane/entities/File.js +1 -1
- package/runtime/arcane/entities/Image.js +8 -5
- package/runtime/arcane/entities/IntentEnvelope.js +20 -17
- package/runtime/arcane/entities/Preference.js +9 -6
- package/runtime/arcane/entities/Theme.js +5 -2
- package/runtime/arcane/entities/User.js +11 -11
- package/runtime/arcane/entities/Weather.js +5 -2
- package/runtime/arcane/modules/AI.js +491 -165
- package/runtime/arcane/modules/AIPreferenceRuntime.js +5 -2
- package/runtime/arcane/modules/AIPreferenceTuple.js +9 -6
- package/runtime/arcane/modules/AIProviderRuntime.js +88 -85
- package/runtime/arcane/modules/AIResponseURLPolicy.js +148 -62
- package/runtime/arcane/modules/AIRuntimeState.js +29 -26
- package/runtime/arcane/modules/AnsiText.js +4 -1
- package/runtime/arcane/modules/ApiModelDatabase.js +22 -19
- package/runtime/arcane/modules/AppDataScope.js +8 -5
- package/runtime/arcane/modules/ArcaneCommunicationBridge.js +4 -1
- package/runtime/arcane/modules/ArcaneNavigationPolicy.js +8 -5
- package/runtime/arcane/modules/ArcaneNetworkPolicy.js +18 -15
- package/runtime/arcane/modules/AsyncBoundary.js +13 -10
- package/runtime/arcane/modules/BrowserTestSuite.js +19 -16
- package/runtime/arcane/modules/CalculatorEngine.js +5 -2
- package/runtime/arcane/modules/ChatRecords.js +18 -15
- package/runtime/arcane/modules/CommunicationAppController.js +9 -6
- package/runtime/arcane/modules/CommunicationHub.js +9 -6
- package/runtime/arcane/modules/CommunicationPreferences.js +4 -1
- package/runtime/arcane/modules/CommunicationProviderRegistry.js +4 -1
- package/runtime/arcane/modules/ComponentContracts.js +56 -53
- package/runtime/arcane/modules/ConfiguredAIChatSession.js +30 -27
- package/runtime/arcane/modules/ConversationActionItems.js +15 -12
- package/runtime/arcane/modules/ConversationClosingReport.js +11 -8
- package/runtime/arcane/modules/ConversationTimebox.js +28 -25
- package/runtime/arcane/modules/CoreLocalModelCatalog.js +17 -14
- package/runtime/arcane/modules/DBLS.js +7 -4
- package/runtime/arcane/modules/DBOPFS.js +7 -7
- package/runtime/arcane/modules/DBOPFSDocumentLibrary.js +34 -31
- package/runtime/arcane/modules/DevelopmentWorkspace.js +4 -1
- package/runtime/arcane/modules/DirectoryPicker.js +8 -5
- package/runtime/arcane/modules/DocumentLexicalSearch.js +11 -8
- package/runtime/arcane/modules/DocumentNavigation.js +7 -4
- package/runtime/arcane/modules/Errors.js +28 -25
- package/runtime/arcane/modules/HTMLImport.js +7 -4
- package/runtime/arcane/modules/IsolatedModelQuestionRunner.js +15 -12
- package/runtime/arcane/modules/LocalAIReadiness.js +21 -18
- package/runtime/arcane/modules/LocalAIReadinessController.js +6 -3
- package/runtime/arcane/modules/MD.js +1 -1
- package/runtime/arcane/modules/Mail.js +46 -43
- package/runtime/arcane/modules/MailOutbox.mjs +48 -45
- package/runtime/arcane/modules/MailTransport.mjs +29 -26
- package/runtime/arcane/modules/MemoryRecords.js +7 -4
- package/runtime/arcane/modules/MessageAdvisory.js +6 -3
- package/runtime/arcane/modules/ModelDefinition.js +7 -4
- package/runtime/arcane/modules/Ollama.js +6 -3
- package/runtime/arcane/modules/OllamaModelIdentifier.js +4 -1
- package/runtime/arcane/modules/OpenMeteoWeatherProvider.js +18 -15
- package/runtime/arcane/modules/PersistentAIChatSession.js +33 -30
- package/runtime/arcane/modules/PreferenceStore.js +20 -17
- package/runtime/arcane/modules/PreparedSpeech.js +485 -0
- package/runtime/arcane/modules/Questionnaire.js +6 -3
- package/runtime/arcane/modules/RecordLinkIndex.js +4 -1
- package/runtime/arcane/modules/RecordPassageIndex.js +9 -6
- package/runtime/arcane/modules/RecordReviewStore.js +12 -9
- package/runtime/arcane/modules/RiskSignalAnalyzer.js +4 -1
- package/runtime/arcane/modules/ScopedOPFSCache.js +6 -3
- package/runtime/arcane/modules/ScreenCapture.js +20 -17
- package/runtime/arcane/modules/SpeechPlayback.js +29 -26
- package/runtime/arcane/modules/StaticDocumentCatalog.js +33 -30
- package/runtime/arcane/modules/SystemAppearance.js +5 -2
- package/runtime/arcane/modules/SystemToolRegistry.js +6 -3
- package/runtime/arcane/modules/TerminalClient.js +14 -11
- package/runtime/arcane/modules/TerminalCommandRegistry.js +4 -1
- package/runtime/arcane/modules/ThemeBootstrap.js +9 -6
- package/runtime/arcane/modules/ThemeManager.js +5 -2
- package/runtime/arcane/modules/TimeGuard.js +1 -1
- package/runtime/arcane/modules/ToolCallRouter.js +11 -8
- package/runtime/arcane/modules/WaitForComponent.js +19 -16
- package/runtime/arcane/modules/YouTubeMedia.js +4 -1
- package/runtime/strong-type/index.js +1276 -352
- package/runtime/strong-type/package.json +69 -45
- package/src/app-descriptor.mjs +17 -14
- package/src/application-tests.mjs +4 -1
- package/src/cli/main.mjs +8 -5
- package/src/constants.mjs +4 -1
- package/src/dev-server.mjs +8 -5
- package/src/doctor.mjs +5 -2
- package/src/dom-event-instrumentation.mjs +24 -20
- package/src/errors.mjs +7 -3
- package/src/event-manager.mjs +111 -108
- package/src/event-queue.mjs +8 -5
- package/src/events.mjs +12 -9
- package/src/import-map.mjs +35 -27
- package/src/installed-sdk-runtime.mjs +4 -1
- package/src/integrated-provider-loader.mjs +9 -6
- package/src/mail-credentials.mjs +16 -13
- package/src/mail-server.mjs +53 -50
- package/src/mail.mjs +18 -15
- package/src/native-plan.mjs +12 -9
- package/src/native-provider-loader.mjs +7 -4
- package/src/packager/core.mjs +18 -15
- package/src/process.mjs +6 -3
- package/src/release-bundle.mjs +14 -11
- package/src/runtime.mjs +4 -1
- package/src/scaffold.mjs +9 -6
- package/src/sdk-browser-runtime.mjs +4 -1
- package/src/source-server.mjs +17 -14
- package/src/targets/index.mjs +5 -2
- package/src/templates/workspace-template.mjs +13 -6
- package/src/testing-loader.mjs +7 -4
- package/src/testing.mjs +10 -7
- package/src/toolchain.mjs +13 -10
- package/src/update-check.mjs +9 -6
- package/src/workspace-operation-lock.mjs +14 -11
- package/src/workspace-runtime.mjs +4 -1
- package/src/workspace.mjs +17 -14
- package/runtime/arcane/modules/AIResponseLength.js +0 -32
|
@@ -1,5 +1,8 @@
|
|
|
1
|
+
import Is from 'strong-type';
|
|
1
2
|
import {marked} from './Marked.min.js';
|
|
2
3
|
|
|
4
|
+
const is=new Is(false);
|
|
5
|
+
|
|
3
6
|
const BARE_HTTP_URL_PATTERN=/\bhttps?:\/\/[^\s<>\[\]"'`]+/giu;
|
|
4
7
|
const BARE_WWW_URL_PATTERN=/\bwww\.[^\s<>\[\]"'`]+/giu;
|
|
5
8
|
const BARE_EMAIL_PATTERN=/\b[A-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?(?:\.[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?)+\b/giu;
|
|
@@ -9,8 +12,19 @@ const CSS_URL_PATTERN=/\burl\(\s*(?:"([^"]*)"|'([^']*)'|([^)]*))\s*\)/giu;
|
|
|
9
12
|
const MARKDOWN_AUTOLINK_PATTERN=/<((?:[a-z][a-z0-9+.-]{1,31}):[^<>\s]+|[^<>\s@]+@[^<>\s@]+\.[^<>\s@]+)>/giu;
|
|
10
13
|
const MARKDOWN_ESCAPED_PUNCTUATION_PATTERN=
|
|
11
14
|
/\\([!"#$%&'()*+,\-./:;<=>?@\[\]\\^_`{|}~])/gu;
|
|
12
|
-
const
|
|
13
|
-
|
|
15
|
+
const HTML_LINK_ATTRIBUTES=new Set([
|
|
16
|
+
'href','src','action','formaction','poster','data'
|
|
17
|
+
]);
|
|
18
|
+
const RENDERED_BLOCK_ELEMENTS=new Set([
|
|
19
|
+
'address','article','aside','blockquote','br','caption','dd','details',
|
|
20
|
+
'dialog','div','dl','dt','fieldset','figcaption','figure','footer','form',
|
|
21
|
+
'h1','h2','h3','h4','h5','h6','header','hr','li','main','nav','ol','p','pre',
|
|
22
|
+
'section','summary','table','tbody','td','tfoot','th','thead','tr','ul'
|
|
23
|
+
]);
|
|
24
|
+
const RENDERED_BLOCK_BOUNDARY_PATTERN=new RegExp(
|
|
25
|
+
`<\\/?(?:${Array.from(RENDERED_BLOCK_ELEMENTS).join('|')})\\b[^>]*>`,
|
|
26
|
+
'giu'
|
|
27
|
+
);
|
|
14
28
|
const RENDERED_TAG_PATTERN=/<[^>]*>/gu;
|
|
15
29
|
const INVISIBLE_FORMATTING_PATTERN=/\p{Default_Ignorable_Code_Point}+/gu;
|
|
16
30
|
const INVISIBLE_TEXT_CONTROL_PATTERN=
|
|
@@ -71,7 +85,7 @@ function decodeNumericCharacterReference(match,hexadecimal,decimal){
|
|
|
71
85
|
const codePoint=Number.parseInt(raw,radix);
|
|
72
86
|
|
|
73
87
|
if(
|
|
74
|
-
!
|
|
88
|
+
!is.integer(codePoint)
|
|
75
89
|
||codePoint<1
|
|
76
90
|
||codePoint>0x10FFFF
|
|
77
91
|
||(codePoint>=0xD800&&codePoint<=0xDFFF)
|
|
@@ -83,9 +97,19 @@ function decodeNumericCharacterReference(match,hexadecimal,decimal){
|
|
|
83
97
|
}
|
|
84
98
|
|
|
85
99
|
function decodeHTMLCharacterReferences(value=''){
|
|
100
|
+
let decoder;
|
|
101
|
+
|
|
86
102
|
return String(value).replace(
|
|
87
|
-
/&(?:(
|
|
103
|
+
/&(?:([a-z][a-z0-9]*);|#x([0-9a-f]+);?|#([0-9]+);?)/gi,
|
|
88
104
|
function decodeOneCharacterReference(match,name,hexadecimal,decimal){
|
|
105
|
+
if(globalThis.document){
|
|
106
|
+
decoder??=globalThis.document.createElement('textarea');
|
|
107
|
+
// Parse only the reference so literal markup and line endings
|
|
108
|
+
// elsewhere in the original value remain untouched.
|
|
109
|
+
decoder.innerHTML=match;
|
|
110
|
+
return decoder.value;
|
|
111
|
+
}
|
|
112
|
+
|
|
89
113
|
return name
|
|
90
114
|
?decodeNamedCharacterReference(match,name)
|
|
91
115
|
:decodeNumericCharacterReference(
|
|
@@ -130,7 +154,7 @@ function trimUnbalancedClosingCharacters(value=''){
|
|
|
130
154
|
}
|
|
131
155
|
|
|
132
156
|
function trimBareLinkCandidate(value=''){
|
|
133
|
-
let result=
|
|
157
|
+
let result=String(value).trim();
|
|
134
158
|
|
|
135
159
|
result=trimUnbalancedClosingCharacters(result);
|
|
136
160
|
while(/[.,;:!?]$/u.test(result)){
|
|
@@ -141,10 +165,10 @@ function trimBareLinkCandidate(value=''){
|
|
|
141
165
|
return result;
|
|
142
166
|
}
|
|
143
167
|
|
|
144
|
-
function
|
|
168
|
+
function normalizeDecodedAIResponseLink(value='',bare=false){
|
|
145
169
|
let result=bare
|
|
146
170
|
?trimBareLinkCandidate(value)
|
|
147
|
-
:
|
|
171
|
+
:String(value).trim();
|
|
148
172
|
|
|
149
173
|
if(result.startsWith('<')&&result.endsWith('>')){
|
|
150
174
|
result=result.slice(1,-1).trim();
|
|
@@ -160,6 +184,13 @@ function normalizeAIResponseLink(value='',{bare=false}={}){
|
|
|
160
184
|
return result;
|
|
161
185
|
}
|
|
162
186
|
|
|
187
|
+
function normalizeAIResponseLink(value='',{bare=false}={}){
|
|
188
|
+
return normalizeDecodedAIResponseLink(
|
|
189
|
+
decodeHTMLCharacterReferences(value),
|
|
190
|
+
bare
|
|
191
|
+
);
|
|
192
|
+
}
|
|
193
|
+
|
|
163
194
|
function normalizeMarkdownDestination(value=''){
|
|
164
195
|
return String(value).replace(
|
|
165
196
|
MARKDOWN_ESCAPED_PUNCTUATION_PATTERN,
|
|
@@ -241,6 +272,28 @@ function extractSrcsetCandidates(value=''){
|
|
|
241
272
|
return candidates;
|
|
242
273
|
}
|
|
243
274
|
|
|
275
|
+
function* extractHTMLLinkAttributes(text=''){
|
|
276
|
+
for(const [pattern,kind] of [
|
|
277
|
+
[HTML_LINK_ATTRIBUTE_PATTERN,'html-attribute'],
|
|
278
|
+
[HTML_SRCSET_ATTRIBUTE_PATTERN,'html-srcset']
|
|
279
|
+
]){
|
|
280
|
+
for(const match of String(text).matchAll(pattern)){
|
|
281
|
+
const value=match[1]??match[2]??match[3]??'';
|
|
282
|
+
const start=(match.index||0)+Math.max(0,match[0].indexOf(value));
|
|
283
|
+
|
|
284
|
+
yield {
|
|
285
|
+
values:kind==='html-srcset'
|
|
286
|
+
?extractSrcsetCandidates(value)
|
|
287
|
+
:[value],
|
|
288
|
+
start,
|
|
289
|
+
end:start+value.length,
|
|
290
|
+
attributeStart:match.index||0,
|
|
291
|
+
kind
|
|
292
|
+
};
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
|
|
244
297
|
function extractMarkdownReferenceDestinations(text=''){
|
|
245
298
|
const source=String(text);
|
|
246
299
|
const destinations=[];
|
|
@@ -339,32 +392,76 @@ function extractRenderedMarkdownLinks(text=''){
|
|
|
339
392
|
pedantic:false
|
|
340
393
|
});
|
|
341
394
|
const links=[];
|
|
395
|
+
const template=globalThis.document?.createElement('template');
|
|
396
|
+
let renderedText;
|
|
342
397
|
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
start:match.index||0,
|
|
347
|
-
kind:'rendered-html-attribute'
|
|
348
|
-
});
|
|
349
|
-
}
|
|
398
|
+
if(template?.content){
|
|
399
|
+
template.innerHTML=rendered;
|
|
400
|
+
const textParts=[];
|
|
350
401
|
|
|
351
|
-
|
|
352
|
-
|
|
402
|
+
function inspectRenderedNode(node){
|
|
403
|
+
if(node.nodeType===3){
|
|
404
|
+
textParts.push(node.data);
|
|
405
|
+
return;
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
const block=RENDERED_BLOCK_ELEMENTS.has(node.localName);
|
|
409
|
+
if(block){
|
|
410
|
+
textParts.push(' ');
|
|
411
|
+
}
|
|
412
|
+
for(const attribute of node.attributes||[]){
|
|
413
|
+
const srcset=attribute.name==='srcset';
|
|
414
|
+
if(!srcset&&!HTML_LINK_ATTRIBUTES.has(attribute.name)){
|
|
415
|
+
continue;
|
|
416
|
+
}
|
|
417
|
+
const values=srcset
|
|
418
|
+
?extractSrcsetCandidates(attribute.value)
|
|
419
|
+
:[attribute.value];
|
|
420
|
+
|
|
421
|
+
for(const value of values){
|
|
422
|
+
links.push({
|
|
423
|
+
value,
|
|
424
|
+
// DOM has no source offsets. Rendered-only candidates
|
|
425
|
+
// follow document order after authored source links.
|
|
426
|
+
start:links.length,
|
|
427
|
+
kind:srcset
|
|
428
|
+
?'rendered-html-srcset'
|
|
429
|
+
:'rendered-html-attribute',
|
|
430
|
+
decoded:true
|
|
431
|
+
});
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
const content=node.localName==='template'&&node.content
|
|
435
|
+
?node.content
|
|
436
|
+
:node;
|
|
437
|
+
for(const child of content.childNodes){
|
|
438
|
+
inspectRenderedNode(child);
|
|
439
|
+
}
|
|
440
|
+
if(block){
|
|
441
|
+
textParts.push(' ');
|
|
442
|
+
}
|
|
443
|
+
}
|
|
353
444
|
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
445
|
+
inspectRenderedNode(template.content);
|
|
446
|
+
renderedText=textParts.join('');
|
|
447
|
+
}else{
|
|
448
|
+
for(const attribute of extractHTMLLinkAttributes(rendered)){
|
|
449
|
+
for(const value of attribute.values){
|
|
450
|
+
links.push({
|
|
451
|
+
value,
|
|
452
|
+
start:attribute.attributeStart,
|
|
453
|
+
kind:`rendered-${attribute.kind}`
|
|
454
|
+
});
|
|
455
|
+
}
|
|
360
456
|
}
|
|
457
|
+
renderedText=decodeHTMLCharacterReferences(
|
|
458
|
+
rendered
|
|
459
|
+
.replace(RENDERED_BLOCK_BOUNDARY_PATTERN,' ')
|
|
460
|
+
.replace(RENDERED_TAG_PATTERN,'')
|
|
461
|
+
);
|
|
361
462
|
}
|
|
362
463
|
|
|
363
|
-
const visibleText=
|
|
364
|
-
rendered
|
|
365
|
-
.replace(RENDERED_BLOCK_BOUNDARY_PATTERN,' ')
|
|
366
|
-
.replace(RENDERED_TAG_PATTERN,'')
|
|
367
|
-
)
|
|
464
|
+
const visibleText=renderedText
|
|
368
465
|
.replace(INVISIBLE_FORMATTING_PATTERN,'')
|
|
369
466
|
.replace(INVISIBLE_TEXT_CONTROL_PATTERN,'');
|
|
370
467
|
const visibleWebRanges=[];
|
|
@@ -383,7 +480,8 @@ function extractRenderedMarkdownLinks(text=''){
|
|
|
383
480
|
value:match[0],
|
|
384
481
|
start,
|
|
385
482
|
kind:'rendered-visible-http',
|
|
386
|
-
bare:true
|
|
483
|
+
bare:true,
|
|
484
|
+
decoded:true
|
|
387
485
|
});
|
|
388
486
|
}
|
|
389
487
|
|
|
@@ -398,7 +496,8 @@ function extractRenderedMarkdownLinks(text=''){
|
|
|
398
496
|
value:`http://${match[0]}`,
|
|
399
497
|
start,
|
|
400
498
|
kind:'rendered-visible-www',
|
|
401
|
-
bare:true
|
|
499
|
+
bare:true,
|
|
500
|
+
decoded:true
|
|
402
501
|
});
|
|
403
502
|
}
|
|
404
503
|
|
|
@@ -412,7 +511,8 @@ function extractRenderedMarkdownLinks(text=''){
|
|
|
412
511
|
value:`mailto:${match[0]}`,
|
|
413
512
|
start,
|
|
414
513
|
kind:'rendered-visible-email',
|
|
415
|
-
bare:false
|
|
514
|
+
bare:false,
|
|
515
|
+
decoded:true
|
|
416
516
|
});
|
|
417
517
|
}
|
|
418
518
|
|
|
@@ -422,7 +522,7 @@ function extractRenderedMarkdownLinks(text=''){
|
|
|
422
522
|
/**
|
|
423
523
|
* Returns both authored URL-like text and destinations produced by the same
|
|
424
524
|
* Markdown parser used by Arcane chat. Values stay exact after renderer-level
|
|
425
|
-
* entity and escape decoding; URL canonicalization would
|
|
525
|
+
* entity and escape decoding; URL canonicalization would change comparison.
|
|
426
526
|
*/
|
|
427
527
|
function extractAIResponseLinks(text=''){
|
|
428
528
|
const source=String(text);
|
|
@@ -436,16 +536,19 @@ function extractAIResponseLinks(text=''){
|
|
|
436
536
|
start=0,
|
|
437
537
|
kind='unknown',
|
|
438
538
|
bare=false,
|
|
439
|
-
allowEmpty=false
|
|
539
|
+
allowEmpty=false,
|
|
540
|
+
decoded=false
|
|
440
541
|
){
|
|
441
|
-
const normalized=
|
|
542
|
+
const normalized=decoded
|
|
543
|
+
?normalizeDecodedAIResponseLink(value,bare)
|
|
544
|
+
:normalizeAIResponseLink(value,{bare});
|
|
442
545
|
|
|
443
546
|
if((!normalized&&!allowEmpty)||seen.has(normalized)){
|
|
444
547
|
return false;
|
|
445
548
|
}
|
|
446
549
|
|
|
447
550
|
seen.add(normalized);
|
|
448
|
-
links.push(
|
|
551
|
+
links.push({value:normalized,start,kind});
|
|
449
552
|
return true;
|
|
450
553
|
}
|
|
451
554
|
|
|
@@ -508,28 +611,10 @@ function extractAIResponseLinks(text=''){
|
|
|
508
611
|
);
|
|
509
612
|
}
|
|
510
613
|
|
|
511
|
-
for(const
|
|
512
|
-
|
|
513
|
-
const
|
|
514
|
-
|
|
515
|
-
const start=(match.index||0)+Math.max(0,offset);
|
|
516
|
-
|
|
517
|
-
addStructuredCandidate(
|
|
518
|
-
value,
|
|
519
|
-
start,
|
|
520
|
-
start+value.length,
|
|
521
|
-
'html-attribute',
|
|
522
|
-
);
|
|
523
|
-
}
|
|
524
|
-
|
|
525
|
-
for(const match of source.matchAll(HTML_SRCSET_ATTRIBUTE_PATTERN)){
|
|
526
|
-
const value=match[1]??match[2]??match[3]??'';
|
|
527
|
-
const offset=(match.index||0)+Math.max(0,match[0].indexOf(value));
|
|
528
|
-
|
|
529
|
-
structuredRanges.push({start:offset,end:offset+value.length});
|
|
530
|
-
|
|
531
|
-
for(const candidate of extractSrcsetCandidates(value)){
|
|
532
|
-
addCandidate(candidate,offset,'html-srcset',false);
|
|
614
|
+
for(const attribute of extractHTMLLinkAttributes(source)){
|
|
615
|
+
structuredRanges.push({start:attribute.start,end:attribute.end});
|
|
616
|
+
for(const value of attribute.values){
|
|
617
|
+
addCandidate(value,attribute.start,attribute.kind,false);
|
|
533
618
|
}
|
|
534
619
|
}
|
|
535
620
|
|
|
@@ -578,7 +663,8 @@ function extractAIResponseLinks(text=''){
|
|
|
578
663
|
source.length+renderedLink.start,
|
|
579
664
|
renderedLink.kind,
|
|
580
665
|
renderedLink.bare===true,
|
|
581
|
-
true
|
|
666
|
+
true,
|
|
667
|
+
renderedLink.decoded===true
|
|
582
668
|
);
|
|
583
669
|
}
|
|
584
670
|
|
|
@@ -586,7 +672,7 @@ function extractAIResponseLinks(text=''){
|
|
|
586
672
|
return left.start-right.start||left.value.localeCompare(right.value);
|
|
587
673
|
});
|
|
588
674
|
|
|
589
|
-
return
|
|
675
|
+
return links;
|
|
590
676
|
}
|
|
591
677
|
|
|
592
678
|
function normalizeAllowedLinks(allowedLinks=[]){
|
|
@@ -610,12 +696,12 @@ function auditAIResponseLinks(text='',allowedLinks=[]){
|
|
|
610
696
|
return !allowed.has(link.value);
|
|
611
697
|
});
|
|
612
698
|
|
|
613
|
-
return
|
|
699
|
+
return {
|
|
614
700
|
ok:unsupportedLinks.length===0,
|
|
615
701
|
links,
|
|
616
|
-
unsupportedLinks
|
|
617
|
-
allowedLinks:
|
|
618
|
-
}
|
|
702
|
+
unsupportedLinks,
|
|
703
|
+
allowedLinks:Array.from(allowed)
|
|
704
|
+
};
|
|
619
705
|
}
|
|
620
706
|
|
|
621
707
|
export {
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import Is from 'strong-type';
|
|
2
|
+
const is=new Is(false);
|
|
3
|
+
|
|
1
4
|
import {createArcaneEventSource} from 'arcane-os/event-manager';
|
|
2
5
|
|
|
3
6
|
const completeValue=(value)=>value;
|
|
@@ -100,7 +103,7 @@ function fail(message) {
|
|
|
100
103
|
}
|
|
101
104
|
|
|
102
105
|
function assertClosedRecord(value, expectedKeys, label) {
|
|
103
|
-
if (!value ||
|
|
106
|
+
if (!value || !is.object(value) || is.array(value)) {
|
|
104
107
|
fail(`${label} must be a plain object.`);
|
|
105
108
|
}
|
|
106
109
|
|
|
@@ -111,7 +114,7 @@ function assertClosedRecord(value, expectedKeys, label) {
|
|
|
111
114
|
|
|
112
115
|
const ownKeys = Reflect.ownKeys(value);
|
|
113
116
|
if (ownKeys.some(function hasSymbolKey(key) {
|
|
114
|
-
return
|
|
117
|
+
return is.symbol(key);
|
|
115
118
|
})) {
|
|
116
119
|
fail(`${label} must not contain symbol keys.`);
|
|
117
120
|
}
|
|
@@ -134,7 +137,7 @@ function assertClosedRecord(value, expectedKeys, label) {
|
|
|
134
137
|
}
|
|
135
138
|
|
|
136
139
|
function nextRevision(value) {
|
|
137
|
-
if (
|
|
140
|
+
if (is.bigint(value)) {
|
|
138
141
|
return value + 1n;
|
|
139
142
|
}
|
|
140
143
|
if (value === Number.MAX_SAFE_INTEGER) {
|
|
@@ -144,7 +147,7 @@ function nextRevision(value) {
|
|
|
144
147
|
}
|
|
145
148
|
|
|
146
149
|
function assertClosedOptions(value, allowedKeys, label) {
|
|
147
|
-
if (!value ||
|
|
150
|
+
if (!value || !is.object(value) || is.array(value)) {
|
|
148
151
|
fail(`${label} must be a plain object.`);
|
|
149
152
|
}
|
|
150
153
|
|
|
@@ -155,7 +158,7 @@ function assertClosedOptions(value, allowedKeys, label) {
|
|
|
155
158
|
|
|
156
159
|
const descriptors = Object.getOwnPropertyDescriptors(value);
|
|
157
160
|
for (const key of Reflect.ownKeys(value)) {
|
|
158
|
-
if (
|
|
161
|
+
if (is.symbol(key) || !allowedKeys.includes(key)) {
|
|
159
162
|
fail(`${label} contains an unknown option.`);
|
|
160
163
|
}
|
|
161
164
|
if (!Object.hasOwn(descriptors[key], 'value')) {
|
|
@@ -169,7 +172,7 @@ function assertNullableIdentifier(value, label) {
|
|
|
169
172
|
return;
|
|
170
173
|
}
|
|
171
174
|
|
|
172
|
-
if (
|
|
175
|
+
if (!is.string(value)
|
|
173
176
|
|| value.length < 1
|
|
174
177
|
|| value.trim() !== value) {
|
|
175
178
|
fail(`${label} must be null or a nonempty trimmed string.`);
|
|
@@ -187,9 +190,9 @@ function copyProgress(progress) {
|
|
|
187
190
|
return null;
|
|
188
191
|
}
|
|
189
192
|
|
|
190
|
-
const suppliedKeys=Reflect.ownKeys(progress).filter(key=>
|
|
193
|
+
const suppliedKeys=Reflect.ownKeys(progress).filter(key=>is.string(key));
|
|
191
194
|
assertClosedOptions(progress, suppliedKeys, 'progress');
|
|
192
|
-
if (
|
|
195
|
+
if (!is.string(progress.phase)
|
|
193
196
|
|| progress.phase.length < 1
|
|
194
197
|
|| progress.phase.trim() !== progress.phase) {
|
|
195
198
|
fail('progress.phase must be a nonempty trimmed string.');
|
|
@@ -199,23 +202,23 @@ function copyProgress(progress) {
|
|
|
199
202
|
const hasUnit = Object.hasOwn(progress, 'unit');
|
|
200
203
|
const hasHeartbeat = Object.hasOwn(progress, 'heartbeat');
|
|
201
204
|
if (hasCompleted
|
|
202
|
-
&& (!
|
|
205
|
+
&& (!is.safeInteger(progress.completed) || progress.completed < 0)) {
|
|
203
206
|
fail('progress.completed must be a nonnegative safe integer.');
|
|
204
207
|
}
|
|
205
208
|
if (hasTotal
|
|
206
209
|
&& progress.total !== null
|
|
207
|
-
&& (!
|
|
210
|
+
&& (!is.safeInteger(progress.total)
|
|
208
211
|
|| progress.total < 0
|
|
209
212
|
|| (hasCompleted && progress.total < progress.completed))) {
|
|
210
213
|
fail('progress.total must be null or a nonnegative safe integer no smaller than progress.completed when completed is present.');
|
|
211
214
|
}
|
|
212
215
|
if (hasUnit
|
|
213
|
-
&& (
|
|
216
|
+
&& (!is.string(progress.unit)
|
|
214
217
|
|| progress.unit.length < 1
|
|
215
218
|
|| progress.unit.trim() !== progress.unit)) {
|
|
216
219
|
fail('progress.unit must be a nonempty trimmed string.');
|
|
217
220
|
}
|
|
218
|
-
if (hasHeartbeat &&
|
|
221
|
+
if (hasHeartbeat && !is.boolean(progress.heartbeat)) {
|
|
219
222
|
fail('progress.heartbeat must be a boolean.');
|
|
220
223
|
}
|
|
221
224
|
|
|
@@ -232,12 +235,12 @@ function copyError(error) {
|
|
|
232
235
|
}
|
|
233
236
|
|
|
234
237
|
assertClosedRecord(error, ERROR_KEYS, 'error');
|
|
235
|
-
if (
|
|
238
|
+
if (!is.string(error.code)
|
|
236
239
|
|| error.code.length < 1
|
|
237
240
|
|| error.code.trim() !== error.code) {
|
|
238
241
|
fail('error.code must be a nonempty trimmed string.');
|
|
239
242
|
}
|
|
240
|
-
if (
|
|
243
|
+
if (!is.string(error.message)
|
|
241
244
|
|| error.message.length < 1) {
|
|
242
245
|
fail('error.message must be a nonempty string.');
|
|
243
246
|
}
|
|
@@ -262,13 +265,13 @@ function copyRoleRecord(role, record) {
|
|
|
262
265
|
|
|
263
266
|
assertNullableIdentifier(record.providerId, 'role state.providerId');
|
|
264
267
|
assertNullableIdentifier(record.modelId, 'role state.modelId');
|
|
265
|
-
if (record.localOnly !== null &&
|
|
268
|
+
if (record.localOnly !== null && !is.boolean(record.localOnly)) {
|
|
266
269
|
fail('role state.localOnly must be null or a boolean.');
|
|
267
270
|
}
|
|
268
|
-
if (
|
|
271
|
+
if (!is.boolean(record.loaded)) {
|
|
269
272
|
fail('role state.loaded must be a boolean.');
|
|
270
273
|
}
|
|
271
|
-
if (
|
|
274
|
+
if (!is.boolean(record.busy)) {
|
|
272
275
|
fail('role state.busy must be a boolean.');
|
|
273
276
|
}
|
|
274
277
|
assertNullableIdentifier(record.operationId, 'role state.operationId');
|
|
@@ -418,10 +421,10 @@ function assertAbortSignal(signal) {
|
|
|
418
421
|
return;
|
|
419
422
|
}
|
|
420
423
|
|
|
421
|
-
if (
|
|
422
|
-
||
|
|
423
|
-
||
|
|
424
|
-
||
|
|
424
|
+
if (!is.object(signal)
|
|
425
|
+
|| !is.boolean(signal.aborted)
|
|
426
|
+
|| !is.function(signal.addEventListener)
|
|
427
|
+
|| !is.function(signal.removeEventListener)) {
|
|
425
428
|
fail('subscription signal must be an AbortSignal.');
|
|
426
429
|
}
|
|
427
430
|
}
|
|
@@ -440,7 +443,7 @@ function stateSubscriptionOptions(options) {
|
|
|
440
443
|
? options.emitCurrent
|
|
441
444
|
: true;
|
|
442
445
|
assertAbortSignal(signal);
|
|
443
|
-
if (
|
|
446
|
+
if (!is.boolean(emitCurrent)) {
|
|
444
447
|
fail('subscription options.emitCurrent must be a boolean.');
|
|
445
448
|
}
|
|
446
449
|
|
|
@@ -492,13 +495,13 @@ function startupOptions(options) {
|
|
|
492
495
|
? options.startTranscription
|
|
493
496
|
: false;
|
|
494
497
|
const signal = Object.hasOwn(options, 'signal') ? options.signal : null;
|
|
495
|
-
if (
|
|
498
|
+
if (!is.boolean(startLanguageModel)) {
|
|
496
499
|
fail('startup options.startLanguageModel must be a boolean.');
|
|
497
500
|
}
|
|
498
|
-
if (
|
|
501
|
+
if (!is.boolean(startMuted)) {
|
|
499
502
|
fail('startup options.startMuted must be a boolean.');
|
|
500
503
|
}
|
|
501
|
-
if (
|
|
504
|
+
if (!is.boolean(startTranscription)) {
|
|
502
505
|
fail('startup options.startTranscription must be a boolean.');
|
|
503
506
|
}
|
|
504
507
|
assertAbortSignal(signal);
|
|
@@ -591,7 +594,7 @@ function normalizedAIRuntimeStartupAbort() {
|
|
|
591
594
|
}
|
|
592
595
|
|
|
593
596
|
function assertListener(listener) {
|
|
594
|
-
if (
|
|
597
|
+
if (!is.function(listener)) {
|
|
595
598
|
fail('listener must be a function.');
|
|
596
599
|
}
|
|
597
600
|
}
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import Is from 'strong-type';
|
|
2
|
+
const is=new Is(false);
|
|
3
|
+
|
|
1
4
|
const ANSI_PATTERN=/\x1b\[([0-9;?]*)([ -\/]*)([@-~])/g;
|
|
2
5
|
const COLORS=['black','red','green','yellow','blue','magenta','cyan','white'];
|
|
3
6
|
|
|
@@ -24,7 +27,7 @@ function applyCodes(style,raw=''){
|
|
|
24
27
|
else if(code>=40&&code<=47) style.background=COLORS[code-40];
|
|
25
28
|
else if(code>=90&&code<=97) style.foreground=`bright-${COLORS[code-90]}`;
|
|
26
29
|
else if(code>=100&&code<=107) style.background=`bright-${COLORS[code-100]}`;
|
|
27
|
-
else if((code===38||code===48)&&codes[index+1]===5&&
|
|
30
|
+
else if((code===38||code===48)&&codes[index+1]===5&&is.integer(codes[index+2])){
|
|
28
31
|
style[code===38?'foreground':'background']=`index-${Math.max(0,Math.min(255,codes[index+2]))}`;
|
|
29
32
|
index+=2;
|
|
30
33
|
}
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import Is from 'strong-type';
|
|
2
|
+
const is=new Is(false);
|
|
3
|
+
|
|
1
4
|
import {createArcaneEventSource} from 'arcane-os/event-manager';
|
|
2
5
|
import ApiModelRecord from '../entities/ApiModelRecord.js';
|
|
3
6
|
|
|
@@ -38,7 +41,7 @@ function endpoint(value){
|
|
|
38
41
|
function appendParameters(url,parameters={}){
|
|
39
42
|
for(const [key,value] of Object.entries(parameters||{})){
|
|
40
43
|
if(value===undefined||value===null||value==='')continue;
|
|
41
|
-
url.searchParams.set(key,
|
|
44
|
+
url.searchParams.set(key,is.array(value)?value.join(','):String(value));
|
|
42
45
|
}
|
|
43
46
|
return url;
|
|
44
47
|
}
|
|
@@ -55,21 +58,21 @@ function signalLike(value){
|
|
|
55
58
|
return value===undefined
|
|
56
59
|
||value===null
|
|
57
60
|
||(
|
|
58
|
-
|
|
59
|
-
&&
|
|
60
|
-
&&
|
|
61
|
-
&&
|
|
61
|
+
is.object(value)
|
|
62
|
+
&&is.boolean(value.aborted)
|
|
63
|
+
&&is.function(value.addEventListener)
|
|
64
|
+
&&is.function(value.removeEventListener)
|
|
62
65
|
);
|
|
63
66
|
}
|
|
64
67
|
|
|
65
68
|
function errorMessage(value,fallback){
|
|
66
|
-
if(
|
|
67
|
-
if(
|
|
69
|
+
if(is.string(value?.message)&&value.message.trim())return value.message;
|
|
70
|
+
if(is.string(value)&&value.trim())return value;
|
|
68
71
|
return fallback;
|
|
69
72
|
}
|
|
70
73
|
|
|
71
74
|
function defineErrorContract(error,contract,cause){
|
|
72
|
-
const priorCode=
|
|
75
|
+
const priorCode=is.string(error?.code)&&error.code?error.code:null;
|
|
73
76
|
try{
|
|
74
77
|
if(priorCode&&priorCode!==contract.code&&!Object.hasOwn(error,'providerCode')){
|
|
75
78
|
Object.defineProperty(error,'providerCode',{configurable:true,enumerable:false,value:priorCode,writable:true});
|
|
@@ -92,7 +95,7 @@ function defineErrorContract(error,contract,cause){
|
|
|
92
95
|
}
|
|
93
96
|
|
|
94
97
|
function apiModelError(error,contract,message){
|
|
95
|
-
const candidate=error&&(
|
|
98
|
+
const candidate=error&&(is.object(error)||is.function(error))
|
|
96
99
|
?error
|
|
97
100
|
:new Error(errorMessage(error,message));
|
|
98
101
|
return defineErrorContract(candidate,contract,error!==candidate?error:undefined);
|
|
@@ -108,7 +111,7 @@ function disposedError(){
|
|
|
108
111
|
|
|
109
112
|
function requestOptions(value){
|
|
110
113
|
if(value===undefined)return {operationId:null,signal:null};
|
|
111
|
-
if(!value
|
|
114
|
+
if(!value||!is.object(value)||is.array(value)){
|
|
112
115
|
throw apiModelError(
|
|
113
116
|
new TypeError('API model request options must be an object.'),
|
|
114
117
|
apiModelErrors.requestOptionsInvalid,
|
|
@@ -124,7 +127,7 @@ function requestOptions(value){
|
|
|
124
127
|
}
|
|
125
128
|
const operationId=value.operationId??null;
|
|
126
129
|
if(operationId!==null&&(
|
|
127
|
-
|
|
130
|
+
!is.string(operationId)
|
|
128
131
|
||operationId.trim()!==operationId
|
|
129
132
|
||operationId.length<1
|
|
130
133
|
)){
|
|
@@ -156,10 +159,10 @@ function operationError(error,stage,signal){
|
|
|
156
159
|
if(signal.aborted){
|
|
157
160
|
const reason=signal.reason;
|
|
158
161
|
if(reason
|
|
159
|
-
&&
|
|
160
|
-
&&
|
|
162
|
+
&&is.object(reason)
|
|
163
|
+
&&is.string(reason.code)
|
|
161
164
|
&&reason.code
|
|
162
|
-
&&
|
|
165
|
+
&&is.string(reason.reason)
|
|
163
166
|
&&reason.reason)return reason;
|
|
164
167
|
return apiModelError(reason??error,apiModelErrors.requestAborted,'The API model request was aborted.');
|
|
165
168
|
}
|
|
@@ -180,8 +183,8 @@ export default class ApiModelDatabase extends EventTarget{
|
|
|
180
183
|
constructor({endpoint:source,parser=value=>value,fetchImpl=globalThis.fetch,cache=null,request={}}={}){
|
|
181
184
|
super();
|
|
182
185
|
this.endpoint=endpoint(source);
|
|
183
|
-
if(
|
|
184
|
-
if(
|
|
186
|
+
if(!is.function(parser))throw new TypeError('API model parser must be a function.');
|
|
187
|
+
if(!is.function(fetchImpl))throw new TypeError('API model fetch implementation must be a function.');
|
|
185
188
|
this.parser=parser;
|
|
186
189
|
this.fetchImpl=fetchImpl;
|
|
187
190
|
this.cache=cache;
|
|
@@ -291,7 +294,7 @@ export default class ApiModelDatabase extends EventTarget{
|
|
|
291
294
|
});
|
|
292
295
|
if(operation.controller.signal.aborted)throw operation.controller.signal.reason;
|
|
293
296
|
stage='response';
|
|
294
|
-
if(!response
|
|
297
|
+
if(!response||!is.object(response)||!is.function(response.json)){
|
|
295
298
|
throw new TypeError('The API model fetch implementation returned an invalid response.');
|
|
296
299
|
}
|
|
297
300
|
stage='response-json';
|
|
@@ -299,9 +302,9 @@ export default class ApiModelDatabase extends EventTarget{
|
|
|
299
302
|
if(operation.controller.signal.aborted)throw operation.controller.signal.reason;
|
|
300
303
|
stage='response-status';
|
|
301
304
|
if(!response.ok){
|
|
302
|
-
const reason=
|
|
305
|
+
const reason=is.string(raw?.reason)&&raw.reason.trim()
|
|
303
306
|
?raw.reason
|
|
304
|
-
:
|
|
307
|
+
:is.string(raw?.error)&&raw.error.trim()
|
|
305
308
|
?raw.error
|
|
306
309
|
:`API request failed (${response.status}).`;
|
|
307
310
|
throw new Error(reason);
|