arcane-os 0.3.1 → 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 +14 -0
- package/README.md +86 -117
- package/bin/arcane-test.mjs +170 -46
- package/browser-runtime/ai/browser-speech-artifacts.mjs +855 -895
- package/browser-runtime/ai/browser-speech-providers.mjs +80 -204
- 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 -148
- package/browser-runtime/ai/speech-worker-runtime.mjs +642 -374
- 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 +1042 -427
- package/runtime/arcane/modules/AIProviderRuntime.js +618 -359
- 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 +27 -67
- 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 +109 -1558
- 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 -185
- package/docs/reference/ai/browser-speech-package-authority.json +0 -835
- package/docs/reference/ai/browser-speech.md +0 -1295
- 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 -719
- package/docs/reference/runtime-components.md +0 -1366
- package/docs/reference/runtime-entities.md +0 -303
- package/docs/reference/runtime-modules.md +0 -2965
- package/docs/reference/sdk-api.md +0 -6698
- package/docs/roadmap.md +0 -79
- package/docs/work-amplification.md +0 -129
- package/runtime/ARCANE_RUNTIME_RELEASE.json +0 -826
|
@@ -1,12 +1,10 @@
|
|
|
1
|
-
const RESPONSE_LENGTH_HEADING='## Application-selected response length';
|
|
2
|
-
|
|
3
1
|
export const AI_RESPONSE_LENGTH_DEFAULT='medium';
|
|
4
2
|
|
|
5
|
-
export const AI_RESPONSE_LENGTH_OPTIONS=
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
]
|
|
3
|
+
export const AI_RESPONSE_LENGTH_OPTIONS=[
|
|
4
|
+
{value:'low',label:'Complete'},
|
|
5
|
+
{value:'medium',label:'Complete'},
|
|
6
|
+
{value:'high',label:'Complete'}
|
|
7
|
+
];
|
|
10
8
|
|
|
11
9
|
const RESPONSE_LENGTH_BY_VALUE=new Map(
|
|
12
10
|
AI_RESPONSE_LENGTH_OPTIONS.map(option=>[option.value,option])
|
|
@@ -20,11 +18,8 @@ export function normalizeAIResponseLength(value){
|
|
|
20
18
|
}
|
|
21
19
|
|
|
22
20
|
export function aiResponseLengthInstruction(value){
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
return `${RESPONSE_LENGTH_HEADING}
|
|
27
|
-
${sentenceTarget} A more specific response-length request in the current user message overrides this saved target. Application requirements, tool or function calls, structured response schemas, safety or crisis requirements, evidence and source attribution, uncertainty, warnings, and next steps override the sentence target. Do not omit required information, invent information, or pad the response merely to reach the target.`;
|
|
21
|
+
normalizeAIResponseLength(value);
|
|
22
|
+
return '';
|
|
28
23
|
}
|
|
29
24
|
|
|
30
25
|
export function applyAIResponseLength(systemPrompt,value){
|
|
@@ -32,11 +27,6 @@ export function applyAIResponseLength(systemPrompt,value){
|
|
|
32
27
|
throw new TypeError('systemPrompt must be a string');
|
|
33
28
|
}
|
|
34
29
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
return prompt;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
const instruction=aiResponseLengthInstruction(value);
|
|
41
|
-
return prompt?`${prompt}\n\n${instruction}`:instruction;
|
|
30
|
+
normalizeAIResponseLength(value);
|
|
31
|
+
return systemPrompt;
|
|
42
32
|
}
|
|
@@ -1,18 +1,20 @@
|
|
|
1
1
|
import {createArcaneEventSource} from 'arcane-os/event-manager';
|
|
2
2
|
|
|
3
|
+
const completeValue=(value)=>value;
|
|
4
|
+
|
|
3
5
|
export const AI_RUNTIME_PROTOCOL = 'arcane-ai-runtime-state/1';
|
|
4
6
|
export const AI_RUNTIME_STATE_EVENT = 'arcane-ai-runtime-state';
|
|
5
7
|
export const AI_RUNTIME_INTENT_EVENT = 'arcane-ai-runtime-intent';
|
|
6
8
|
/** Emitted with the text-chat barrier report, not full speech settlement. */
|
|
7
9
|
export const AI_RUNTIME_STARTUP_EVENT = 'arcane-ai-runtime-startup-settled';
|
|
8
10
|
|
|
9
|
-
export const AI_RUNTIME_ROLES =
|
|
11
|
+
export const AI_RUNTIME_ROLES = completeValue([
|
|
10
12
|
'llm',
|
|
11
13
|
'stt',
|
|
12
14
|
'tts'
|
|
13
15
|
]);
|
|
14
16
|
|
|
15
|
-
export const AI_RUNTIME_STATES =
|
|
17
|
+
export const AI_RUNTIME_STATES = completeValue([
|
|
16
18
|
'unavailable',
|
|
17
19
|
'unloaded',
|
|
18
20
|
'loading',
|
|
@@ -22,7 +24,7 @@ export const AI_RUNTIME_STATES = Object.freeze([
|
|
|
22
24
|
'disposed'
|
|
23
25
|
]);
|
|
24
26
|
|
|
25
|
-
const ROLE_KEYS =
|
|
27
|
+
const ROLE_KEYS = completeValue([
|
|
26
28
|
'role',
|
|
27
29
|
'state',
|
|
28
30
|
'providerId',
|
|
@@ -34,30 +36,31 @@ const ROLE_KEYS = Object.freeze([
|
|
|
34
36
|
'progress',
|
|
35
37
|
'error'
|
|
36
38
|
]);
|
|
37
|
-
const PROGRESS_KEYS =
|
|
39
|
+
const PROGRESS_KEYS = completeValue([
|
|
38
40
|
'phase',
|
|
39
41
|
'completed',
|
|
40
42
|
'total',
|
|
41
43
|
'unit',
|
|
42
44
|
'heartbeat'
|
|
43
45
|
]);
|
|
44
|
-
const ERROR_KEYS =
|
|
46
|
+
const ERROR_KEYS = completeValue([
|
|
45
47
|
'code',
|
|
46
48
|
'message'
|
|
47
49
|
]);
|
|
48
|
-
const INTENT_KEYS =
|
|
50
|
+
const INTENT_KEYS = completeValue([
|
|
49
51
|
'role',
|
|
50
52
|
'action',
|
|
51
53
|
'reason'
|
|
52
54
|
]);
|
|
53
|
-
const SUBSCRIPTION_OPTION_KEYS =
|
|
55
|
+
const SUBSCRIPTION_OPTION_KEYS = completeValue([
|
|
54
56
|
'signal',
|
|
55
57
|
'emitCurrent'
|
|
56
58
|
]);
|
|
57
|
-
const INTENT_SUBSCRIPTION_OPTION_KEYS =
|
|
59
|
+
const INTENT_SUBSCRIPTION_OPTION_KEYS = completeValue([
|
|
58
60
|
'signal'
|
|
59
61
|
]);
|
|
60
|
-
const STARTUP_OPTION_KEYS =
|
|
62
|
+
const STARTUP_OPTION_KEYS = completeValue([
|
|
63
|
+
'startLanguageModel',
|
|
61
64
|
'startMuted',
|
|
62
65
|
'startTranscription',
|
|
63
66
|
'signal'
|
|
@@ -85,9 +88,6 @@ const STARTUP_TERMINAL_STATES = new Set([
|
|
|
85
88
|
'error',
|
|
86
89
|
'disposed'
|
|
87
90
|
]);
|
|
88
|
-
const MAX_IDENTIFIER_LENGTH = 128;
|
|
89
|
-
const MAX_PROGRESS_UNIT_LENGTH = 32;
|
|
90
|
-
const MAX_ERROR_MESSAGE_LENGTH = 512;
|
|
91
91
|
|
|
92
92
|
/**
|
|
93
93
|
* @deprecated Subscribe through the focused runtime helpers or arcaneEvents.
|
|
@@ -109,14 +109,14 @@ const aiRuntimeEventSource = createArcaneEventSource(
|
|
|
109
109
|
aiRuntimeEventCompatibilityView,
|
|
110
110
|
{
|
|
111
111
|
source: 'ai-runtime-state',
|
|
112
|
-
eventTypes:
|
|
112
|
+
eventTypes: completeValue([
|
|
113
113
|
AI_RUNTIME_STATE_EVENT,
|
|
114
114
|
AI_RUNTIME_INTENT_EVENT,
|
|
115
115
|
AI_RUNTIME_STARTUP_EVENT
|
|
116
116
|
])
|
|
117
117
|
}
|
|
118
118
|
);
|
|
119
|
-
export const aiRuntimeEvents =
|
|
119
|
+
export const aiRuntimeEvents = completeValue(aiRuntimeEventCompatibilityView);
|
|
120
120
|
|
|
121
121
|
function fail(message) {
|
|
122
122
|
throw new TypeError(`ARCANE_AI_RUNTIME_STATE_INVALID: ${message}`);
|
|
@@ -139,8 +139,8 @@ function assertClosedRecord(value, expectedKeys, label) {
|
|
|
139
139
|
fail(`${label} must not contain symbol keys.`);
|
|
140
140
|
}
|
|
141
141
|
|
|
142
|
-
const actualKeys = ownKeys.
|
|
143
|
-
const requiredKeys = expectedKeys.
|
|
142
|
+
const actualKeys = [...ownKeys].sort();
|
|
143
|
+
const requiredKeys = [...expectedKeys].sort();
|
|
144
144
|
if (actualKeys.length !== requiredKeys.length
|
|
145
145
|
|| actualKeys.some(function hasUnexpectedKey(key, index) {
|
|
146
146
|
return key !== requiredKeys[index];
|
|
@@ -156,6 +156,16 @@ function assertClosedRecord(value, expectedKeys, label) {
|
|
|
156
156
|
}
|
|
157
157
|
}
|
|
158
158
|
|
|
159
|
+
function nextRevision(value) {
|
|
160
|
+
if (typeof value === 'bigint') {
|
|
161
|
+
return value + 1n;
|
|
162
|
+
}
|
|
163
|
+
if (value === Number.MAX_SAFE_INTEGER) {
|
|
164
|
+
return BigInt(value) + 1n;
|
|
165
|
+
}
|
|
166
|
+
return value + 1;
|
|
167
|
+
}
|
|
168
|
+
|
|
159
169
|
function assertClosedOptions(value, allowedKeys, label) {
|
|
160
170
|
if (!value || typeof value !== 'object' || Array.isArray(value)) {
|
|
161
171
|
fail(`${label} must be a plain object.`);
|
|
@@ -184,9 +194,8 @@ function assertNullableIdentifier(value, label) {
|
|
|
184
194
|
|
|
185
195
|
if (typeof value !== 'string'
|
|
186
196
|
|| value.length < 1
|
|
187
|
-
|| value.length > MAX_IDENTIFIER_LENGTH
|
|
188
197
|
|| value.trim() !== value) {
|
|
189
|
-
fail(`${label} must be null or a trimmed
|
|
198
|
+
fail(`${label} must be null or a nonempty trimmed string.`);
|
|
190
199
|
}
|
|
191
200
|
}
|
|
192
201
|
|
|
@@ -204,9 +213,8 @@ function copyProgress(progress) {
|
|
|
204
213
|
assertClosedRecord(progress, PROGRESS_KEYS, 'progress');
|
|
205
214
|
if (typeof progress.phase !== 'string'
|
|
206
215
|
|| progress.phase.length < 1
|
|
207
|
-
|| progress.phase.length > MAX_IDENTIFIER_LENGTH
|
|
208
216
|
|| progress.phase.trim() !== progress.phase) {
|
|
209
|
-
fail(
|
|
217
|
+
fail('progress.phase must be a nonempty trimmed string.');
|
|
210
218
|
}
|
|
211
219
|
if (!Number.isSafeInteger(progress.completed) || progress.completed < 0) {
|
|
212
220
|
fail('progress.completed must be a nonnegative safe integer.');
|
|
@@ -218,15 +226,14 @@ function copyProgress(progress) {
|
|
|
218
226
|
}
|
|
219
227
|
if (typeof progress.unit !== 'string'
|
|
220
228
|
|| progress.unit.length < 1
|
|
221
|
-
|| progress.unit.length > MAX_PROGRESS_UNIT_LENGTH
|
|
222
229
|
|| progress.unit.trim() !== progress.unit) {
|
|
223
|
-
fail(
|
|
230
|
+
fail('progress.unit must be a nonempty trimmed string.');
|
|
224
231
|
}
|
|
225
232
|
if (typeof progress.heartbeat !== 'boolean') {
|
|
226
233
|
fail('progress.heartbeat must be a boolean.');
|
|
227
234
|
}
|
|
228
235
|
|
|
229
|
-
return
|
|
236
|
+
return completeValue(
|
|
230
237
|
{
|
|
231
238
|
phase: progress.phase,
|
|
232
239
|
completed: progress.completed,
|
|
@@ -245,18 +252,15 @@ function copyError(error) {
|
|
|
245
252
|
assertClosedRecord(error, ERROR_KEYS, 'error');
|
|
246
253
|
if (typeof error.code !== 'string'
|
|
247
254
|
|| error.code.length < 1
|
|
248
|
-
|| error.code.length > MAX_IDENTIFIER_LENGTH
|
|
249
255
|
|| error.code.trim() !== error.code) {
|
|
250
|
-
fail(
|
|
256
|
+
fail('error.code must be a nonempty trimmed string.');
|
|
251
257
|
}
|
|
252
258
|
if (typeof error.message !== 'string'
|
|
253
|
-
|| error.message.length < 1
|
|
254
|
-
|
|
255
|
-
|| error.message.trim() !== error.message) {
|
|
256
|
-
fail(`error.message must be a trimmed 1-${MAX_ERROR_MESSAGE_LENGTH} character string.`);
|
|
259
|
+
|| error.message.length < 1) {
|
|
260
|
+
fail('error.message must be a nonempty string.');
|
|
257
261
|
}
|
|
258
262
|
|
|
259
|
-
return
|
|
263
|
+
return completeValue(
|
|
260
264
|
{
|
|
261
265
|
code: error.code,
|
|
262
266
|
message: error.message
|
|
@@ -312,7 +316,7 @@ function copyRoleRecord(role, record) {
|
|
|
312
316
|
fail('only an error role state may include error details.');
|
|
313
317
|
}
|
|
314
318
|
|
|
315
|
-
return
|
|
319
|
+
return completeValue(
|
|
316
320
|
{
|
|
317
321
|
role,
|
|
318
322
|
state: record.state,
|
|
@@ -329,7 +333,7 @@ function copyRoleRecord(role, record) {
|
|
|
329
333
|
}
|
|
330
334
|
|
|
331
335
|
function unavailableRole(role) {
|
|
332
|
-
return
|
|
336
|
+
return completeValue(
|
|
333
337
|
{
|
|
334
338
|
role,
|
|
335
339
|
state: 'unavailable',
|
|
@@ -347,7 +351,7 @@ function unavailableRole(role) {
|
|
|
347
351
|
|
|
348
352
|
function publicAIRuntimeState(snapshot, role = null) {
|
|
349
353
|
const roleState = role === null ? null : snapshot.roles[role];
|
|
350
|
-
return
|
|
354
|
+
return completeValue(
|
|
351
355
|
{
|
|
352
356
|
revision: snapshot.revision,
|
|
353
357
|
...(roleState
|
|
@@ -370,7 +374,7 @@ function publicAIRuntimeState(snapshot, role = null) {
|
|
|
370
374
|
}
|
|
371
375
|
|
|
372
376
|
function publicAIRuntimeIntent(intent) {
|
|
373
|
-
return
|
|
377
|
+
return completeValue(
|
|
374
378
|
{
|
|
375
379
|
role: intent.role,
|
|
376
380
|
action: intent.action,
|
|
@@ -380,7 +384,7 @@ function publicAIRuntimeIntent(intent) {
|
|
|
380
384
|
}
|
|
381
385
|
|
|
382
386
|
function publicAIRuntimeStartup(report) {
|
|
383
|
-
return
|
|
387
|
+
return completeValue(
|
|
384
388
|
{
|
|
385
389
|
revision: report.currentRevision,
|
|
386
390
|
role: 'llm',
|
|
@@ -390,11 +394,11 @@ function publicAIRuntimeStartup(report) {
|
|
|
390
394
|
}
|
|
391
395
|
|
|
392
396
|
function initialSnapshot() {
|
|
393
|
-
return
|
|
397
|
+
return completeValue(
|
|
394
398
|
{
|
|
395
399
|
protocol: AI_RUNTIME_PROTOCOL,
|
|
396
400
|
revision: 0,
|
|
397
|
-
roles:
|
|
401
|
+
roles: completeValue(
|
|
398
402
|
{
|
|
399
403
|
llm: unavailableRole('llm'),
|
|
400
404
|
stt: unavailableRole('stt'),
|
|
@@ -405,6 +409,26 @@ function initialSnapshot() {
|
|
|
405
409
|
);
|
|
406
410
|
}
|
|
407
411
|
|
|
412
|
+
function copyRuntimeRoleView(record) {
|
|
413
|
+
return {
|
|
414
|
+
...record,
|
|
415
|
+
progress: record.progress === null ? null : {...record.progress},
|
|
416
|
+
error: record.error === null ? null : {...record.error}
|
|
417
|
+
};
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
function copyRuntimeSnapshot(snapshot) {
|
|
421
|
+
return {
|
|
422
|
+
protocol: snapshot.protocol,
|
|
423
|
+
revision: snapshot.revision,
|
|
424
|
+
roles: {
|
|
425
|
+
llm: copyRuntimeRoleView(snapshot.roles.llm),
|
|
426
|
+
stt: copyRuntimeRoleView(snapshot.roles.stt),
|
|
427
|
+
tts: copyRuntimeRoleView(snapshot.roles.tts)
|
|
428
|
+
}
|
|
429
|
+
};
|
|
430
|
+
}
|
|
431
|
+
|
|
408
432
|
let currentSnapshot = initialSnapshot();
|
|
409
433
|
|
|
410
434
|
function assertAbortSignal(signal) {
|
|
@@ -468,6 +492,7 @@ function intentSubscriptionOptions(options) {
|
|
|
468
492
|
function startupOptions(options) {
|
|
469
493
|
if (options === undefined) {
|
|
470
494
|
return {
|
|
495
|
+
startLanguageModel: true,
|
|
471
496
|
startMuted: true,
|
|
472
497
|
startTranscription: false,
|
|
473
498
|
signal: null
|
|
@@ -475,6 +500,9 @@ function startupOptions(options) {
|
|
|
475
500
|
}
|
|
476
501
|
|
|
477
502
|
assertClosedOptions(options, STARTUP_OPTION_KEYS, 'startup options');
|
|
503
|
+
const startLanguageModel = Object.hasOwn(options, 'startLanguageModel')
|
|
504
|
+
? options.startLanguageModel
|
|
505
|
+
: true;
|
|
478
506
|
const startMuted = Object.hasOwn(options, 'startMuted')
|
|
479
507
|
? options.startMuted
|
|
480
508
|
: true;
|
|
@@ -482,6 +510,9 @@ function startupOptions(options) {
|
|
|
482
510
|
? options.startTranscription
|
|
483
511
|
: false;
|
|
484
512
|
const signal = Object.hasOwn(options, 'signal') ? options.signal : null;
|
|
513
|
+
if (typeof startLanguageModel !== 'boolean') {
|
|
514
|
+
fail('startup options.startLanguageModel must be a boolean.');
|
|
515
|
+
}
|
|
485
516
|
if (typeof startMuted !== 'boolean') {
|
|
486
517
|
fail('startup options.startMuted must be a boolean.');
|
|
487
518
|
}
|
|
@@ -491,6 +522,7 @@ function startupOptions(options) {
|
|
|
491
522
|
assertAbortSignal(signal);
|
|
492
523
|
|
|
493
524
|
return {
|
|
525
|
+
startLanguageModel,
|
|
494
526
|
startMuted,
|
|
495
527
|
startTranscription,
|
|
496
528
|
signal
|
|
@@ -501,10 +533,16 @@ function hasAIRuntimeSelection(roleState) {
|
|
|
501
533
|
return roleState.providerId !== null && roleState.modelId !== null;
|
|
502
534
|
}
|
|
503
535
|
|
|
504
|
-
function startupRequestedRoles(
|
|
505
|
-
|
|
536
|
+
function startupRequestedRoles(
|
|
537
|
+
snapshot,
|
|
538
|
+
startLanguageModel,
|
|
539
|
+
startMuted,
|
|
540
|
+
startTranscription
|
|
541
|
+
) {
|
|
542
|
+
return completeValue(
|
|
506
543
|
{
|
|
507
|
-
llm:
|
|
544
|
+
llm: startLanguageModel
|
|
545
|
+
&& hasAIRuntimeSelection(snapshot.roles.llm),
|
|
508
546
|
stt: startTranscription && hasAIRuntimeSelection(snapshot.roles.stt),
|
|
509
547
|
tts: !startMuted && hasAIRuntimeSelection(snapshot.roles.tts)
|
|
510
548
|
}
|
|
@@ -518,7 +556,7 @@ function isAIRuntimeStartupRoleSettled(roleState) {
|
|
|
518
556
|
}
|
|
519
557
|
|
|
520
558
|
function startupRoleReport(requested, roleState) {
|
|
521
|
-
return
|
|
559
|
+
return completeValue(
|
|
522
560
|
{
|
|
523
561
|
requested,
|
|
524
562
|
state: roleState
|
|
@@ -529,19 +567,21 @@ function startupRoleReport(requested, roleState) {
|
|
|
529
567
|
function startupReport(
|
|
530
568
|
snapshot,
|
|
531
569
|
startRevision,
|
|
570
|
+
startLanguageModel,
|
|
532
571
|
startMuted,
|
|
533
572
|
startTranscription,
|
|
534
573
|
requestedRoles
|
|
535
574
|
) {
|
|
536
|
-
return
|
|
575
|
+
return completeValue(
|
|
537
576
|
{
|
|
538
577
|
protocol: AI_RUNTIME_PROTOCOL,
|
|
539
578
|
startRevision,
|
|
540
579
|
currentRevision: snapshot.revision,
|
|
580
|
+
startLanguageModel,
|
|
541
581
|
startMuted,
|
|
542
582
|
startTranscription,
|
|
543
583
|
chatReady: snapshot.roles.llm.state === 'ready',
|
|
544
|
-
roles:
|
|
584
|
+
roles: completeValue(
|
|
545
585
|
{
|
|
546
586
|
llm: startupRoleReport(
|
|
547
587
|
requestedRoles.llm,
|
|
@@ -605,9 +645,9 @@ function subscribe(eventName, listener, normalized, currentValue) {
|
|
|
605
645
|
return unsubscribeAIRuntimeEvent;
|
|
606
646
|
}
|
|
607
647
|
|
|
608
|
-
/** Returns the current
|
|
648
|
+
/** Returns a complete defensive copy of the current runtime-state snapshot. */
|
|
609
649
|
export function getAIRuntimeState() {
|
|
610
|
-
return currentSnapshot;
|
|
650
|
+
return copyRuntimeSnapshot(currentSnapshot);
|
|
611
651
|
}
|
|
612
652
|
|
|
613
653
|
/**
|
|
@@ -619,7 +659,7 @@ export function subscribeAIRuntimeState(listener, options) {
|
|
|
619
659
|
AI_RUNTIME_STATE_EVENT,
|
|
620
660
|
listener,
|
|
621
661
|
stateSubscriptionOptions(options),
|
|
622
|
-
currentSnapshot
|
|
662
|
+
copyRuntimeSnapshot(currentSnapshot)
|
|
623
663
|
);
|
|
624
664
|
}
|
|
625
665
|
|
|
@@ -629,33 +669,27 @@ export function subscribeAIRuntimeState(listener, options) {
|
|
|
629
669
|
*/
|
|
630
670
|
export function publishAIRuntimeRoleState(role, completeRecord) {
|
|
631
671
|
const nextRole = copyRoleRecord(role, completeRecord);
|
|
632
|
-
if (currentSnapshot.revision === Number.MAX_SAFE_INTEGER) {
|
|
633
|
-
throw new RangeError(
|
|
634
|
-
'ARCANE_AI_RUNTIME_STATE_INVALID: state revision exhausted.'
|
|
635
|
-
);
|
|
636
|
-
}
|
|
637
|
-
|
|
638
672
|
const nextRoles = {
|
|
639
673
|
llm: currentSnapshot.roles.llm,
|
|
640
674
|
stt: currentSnapshot.roles.stt,
|
|
641
675
|
tts: currentSnapshot.roles.tts
|
|
642
676
|
};
|
|
643
677
|
nextRoles[role] = nextRole;
|
|
644
|
-
currentSnapshot =
|
|
678
|
+
currentSnapshot = completeValue(
|
|
645
679
|
{
|
|
646
680
|
protocol: AI_RUNTIME_PROTOCOL,
|
|
647
|
-
revision: currentSnapshot.revision
|
|
648
|
-
roles:
|
|
681
|
+
revision: nextRevision(currentSnapshot.revision),
|
|
682
|
+
roles: completeValue(nextRoles)
|
|
649
683
|
}
|
|
650
684
|
);
|
|
651
685
|
aiRuntimeEventSource.dispatch(
|
|
652
686
|
AI_RUNTIME_STATE_EVENT,
|
|
653
|
-
currentSnapshot,
|
|
687
|
+
copyRuntimeSnapshot(currentSnapshot),
|
|
654
688
|
{
|
|
655
689
|
publicDetail: publicAIRuntimeState(currentSnapshot, role)
|
|
656
690
|
}
|
|
657
691
|
);
|
|
658
|
-
return currentSnapshot;
|
|
692
|
+
return copyRuntimeSnapshot(currentSnapshot);
|
|
659
693
|
}
|
|
660
694
|
|
|
661
695
|
/**
|
|
@@ -665,37 +699,32 @@ export function publishAIRuntimeRoleState(role, completeRecord) {
|
|
|
665
699
|
*/
|
|
666
700
|
export function publishAIRuntimeRolesState(completeRecords) {
|
|
667
701
|
assertClosedRecord(completeRecords, AI_RUNTIME_ROLES, 'runtime role states');
|
|
668
|
-
const nextRoles =
|
|
702
|
+
const nextRoles = completeValue(
|
|
669
703
|
{
|
|
670
704
|
llm: copyRoleRecord('llm', completeRecords.llm),
|
|
671
705
|
stt: copyRoleRecord('stt', completeRecords.stt),
|
|
672
706
|
tts: copyRoleRecord('tts', completeRecords.tts)
|
|
673
707
|
}
|
|
674
708
|
);
|
|
675
|
-
|
|
676
|
-
throw new RangeError(
|
|
677
|
-
'ARCANE_AI_RUNTIME_STATE_INVALID: state revision exhausted.'
|
|
678
|
-
);
|
|
679
|
-
}
|
|
680
|
-
currentSnapshot = Object.freeze(
|
|
709
|
+
currentSnapshot = completeValue(
|
|
681
710
|
{
|
|
682
711
|
protocol: AI_RUNTIME_PROTOCOL,
|
|
683
|
-
revision: currentSnapshot.revision
|
|
712
|
+
revision: nextRevision(currentSnapshot.revision),
|
|
684
713
|
roles: nextRoles
|
|
685
714
|
}
|
|
686
715
|
);
|
|
687
716
|
aiRuntimeEventSource.dispatch(
|
|
688
717
|
AI_RUNTIME_STATE_EVENT,
|
|
689
|
-
currentSnapshot,
|
|
718
|
+
copyRuntimeSnapshot(currentSnapshot),
|
|
690
719
|
{
|
|
691
720
|
publicDetail: publicAIRuntimeState(currentSnapshot)
|
|
692
721
|
}
|
|
693
722
|
);
|
|
694
|
-
return currentSnapshot;
|
|
723
|
+
return copyRuntimeSnapshot(currentSnapshot);
|
|
695
724
|
}
|
|
696
725
|
|
|
697
726
|
/**
|
|
698
|
-
* Emits
|
|
727
|
+
* Emits a complete capability-neutral lifecycle request. This function does
|
|
699
728
|
* not execute, authorize, fetch, load, unload, dispose, or select a fallback.
|
|
700
729
|
*/
|
|
701
730
|
export function requestAIRuntimeIntent(intent) {
|
|
@@ -708,7 +737,7 @@ export function requestAIRuntimeIntent(intent) {
|
|
|
708
737
|
fail('runtime intent.reason must be startup, user, or teardown.');
|
|
709
738
|
}
|
|
710
739
|
|
|
711
|
-
const publishedIntent =
|
|
740
|
+
const publishedIntent = completeValue(
|
|
712
741
|
{
|
|
713
742
|
role: intent.role,
|
|
714
743
|
action: intent.action,
|
|
@@ -737,7 +766,9 @@ export function subscribeAIRuntimeIntents(listener, options) {
|
|
|
737
766
|
|
|
738
767
|
/**
|
|
739
768
|
* Starts one observational runtime barrier after preferences and provider
|
|
740
|
-
* routes are hydrated. `
|
|
769
|
+
* routes are hydrated. `startLanguageModel:false` keeps the selected LLM
|
|
770
|
+
* unloaded for an explicit user activation intent. `barrier` resolves when a
|
|
771
|
+
* requested LLM is ready or terminal, or immediately when LLM startup was not requested;
|
|
741
772
|
* `settled` waits for every requested role. Providers own every requested load.
|
|
742
773
|
*/
|
|
743
774
|
export function startAIRuntime(options) {
|
|
@@ -799,6 +830,7 @@ export function startAIRuntime(options) {
|
|
|
799
830
|
const report = startupReport(
|
|
800
831
|
snapshot,
|
|
801
832
|
startRevision,
|
|
833
|
+
normalized.startLanguageModel,
|
|
802
834
|
normalized.startMuted,
|
|
803
835
|
normalized.startTranscription,
|
|
804
836
|
requestedRoles
|
|
@@ -820,6 +852,7 @@ export function startAIRuntime(options) {
|
|
|
820
852
|
startupReport(
|
|
821
853
|
snapshot,
|
|
822
854
|
startRevision,
|
|
855
|
+
normalized.startLanguageModel,
|
|
823
856
|
normalized.startMuted,
|
|
824
857
|
normalized.startTranscription,
|
|
825
858
|
requestedRoles
|
|
@@ -833,7 +866,10 @@ export function startAIRuntime(options) {
|
|
|
833
866
|
}
|
|
834
867
|
|
|
835
868
|
if (!barrierResolved
|
|
836
|
-
&&
|
|
869
|
+
&& (
|
|
870
|
+
!requestedRoles.llm
|
|
871
|
+
|| isAIRuntimeStartupRoleSettled(snapshot.roles.llm)
|
|
872
|
+
)) {
|
|
837
873
|
resolveAIRuntimeStartupBarrier(snapshot);
|
|
838
874
|
}
|
|
839
875
|
if (cancelled) {
|
|
@@ -910,7 +946,7 @@ export function startAIRuntime(options) {
|
|
|
910
946
|
cancelAIRuntimeStartup();
|
|
911
947
|
}
|
|
912
948
|
|
|
913
|
-
const handle =
|
|
949
|
+
const handle = completeValue(
|
|
914
950
|
{
|
|
915
951
|
barrier,
|
|
916
952
|
settled,
|
|
@@ -922,6 +958,7 @@ export function startAIRuntime(options) {
|
|
|
922
958
|
startRevision = latestSnapshot.revision;
|
|
923
959
|
requestedRoles = startupRequestedRoles(
|
|
924
960
|
latestSnapshot,
|
|
961
|
+
normalized.startLanguageModel,
|
|
925
962
|
normalized.startMuted,
|
|
926
963
|
normalized.startTranscription
|
|
927
964
|
);
|