arcane-os 0.84.0 → 0.86.0
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 +41 -0
- package/README.md +1 -1
- package/browser-runtime/ai/model-observation.mjs +315 -0
- package/browser-runtime/core/app-control.mjs +27 -1
- package/docs/reference/README.md +1 -0
- package/docs/reference/cli.md +5 -2
- package/docs/reference/core-native-packaging.md +14 -0
- package/docs/reference/core-runtime.md +20 -11
- package/docs/reference/core-shared-host.md +9 -3
- package/docs/reference/execution-devices.md +6 -0
- package/docs/reference/inventory/package-api.json +49 -7
- package/docs/reference/model-observation.md +220 -0
- package/docs/reference/native-app-control.md +132 -16
- package/docs/reference/sdk-api.md +106 -8
- package/package.json +3 -1
- package/src/cli/app-control.mjs +14 -3
- package/src/core/app-control.mjs +2 -0
- package/src/core/hosts/windows/ArcaneAppControl.cs +2 -1
- package/src/core/hosts/windows/ArcaneWindowControl.cs +216 -31
- package/src/core/runtime-connection.mjs +4 -1
- package/src/core/runtime.mjs +15 -7
- package/src/core/services/model-observation.mjs +231 -0
- package/src/local-ai/execution-devices-windows.ps1 +3 -1
- package/src/native/portable-layout.mjs +16 -0
|
@@ -1,17 +1,59 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": 1,
|
|
3
|
-
"sdkVersion": "0.
|
|
3
|
+
"sdkVersion": "0.85.0",
|
|
4
4
|
"environment": {
|
|
5
5
|
"runtime": "Node.js for Node entrypoints; browser for browser-only entrypoints",
|
|
6
6
|
"minimumVersion": "22.23.2 for Node entrypoints",
|
|
7
7
|
"moduleSystem": "ESM"
|
|
8
8
|
},
|
|
9
|
-
"memberCount":
|
|
9
|
+
"memberCount": 306,
|
|
10
10
|
"runtimeSubpathPatterns": {
|
|
11
11
|
"arcane-os/modules/*": "./runtime/arcane/modules/*",
|
|
12
12
|
"arcane-os/entities/*": "./runtime/arcane/entities/*"
|
|
13
13
|
},
|
|
14
14
|
"members": [
|
|
15
|
+
{
|
|
16
|
+
"id": "core-model-observation:createModelObservationService",
|
|
17
|
+
"name": "createModelObservationService",
|
|
18
|
+
"displayName": "createModelObservationService()",
|
|
19
|
+
"kind": "function",
|
|
20
|
+
"signature": "createModelObservationService({runtime,localAI=null,image=null,decisions=null}={})",
|
|
21
|
+
"entrypoints": ["arcane-os/core/model-observation"],
|
|
22
|
+
"primaryImport": "arcane-os/core/model-observation",
|
|
23
|
+
"group": "Existing model observation",
|
|
24
|
+
"summary": "Observes an existing Core runtime and supplied actual native model service owners without creating or starting them.",
|
|
25
|
+
"availability": "Node with an existing Core runtime and optional existing native model services",
|
|
26
|
+
"protocol": "arcane/1 model.observation current, watch, renderer.attach and renderer.publish methods",
|
|
27
|
+
"normalization": "Returns name:model-observation, current(), dispose() and four methods. Complete current snapshots retain core, optional native localAI/image/decisions and request-owned renderer attachments. Missing owners are null; watch diagnostics defaults false and explicit true exposes complete live failed response frames for supplied native method keys without retained failure history. Actual clientRequestId identifies acknowledgements; coreRequestId keys renderer lifetime. Disposal cancels only observation requests/subscriptions, never models or the supplied runtime. No getService, readiness barrier, model creation, inference, polling or fabricated operation identities."
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
"id": "ai-model-observation:attachModelObservation",
|
|
31
|
+
"name": "attachModelObservation",
|
|
32
|
+
"displayName": "attachModelObservation()",
|
|
33
|
+
"kind": "function",
|
|
34
|
+
"signature": "attachModelObservation({client,aiRuntimeState=null,imageRuntime=null,decisionModel=null,modelController=null,signal,onError}={})",
|
|
35
|
+
"entrypoints": ["arcane-os/ai/model-observation"],
|
|
36
|
+
"primaryImport": "arcane-os/ai/model-observation",
|
|
37
|
+
"group": "Existing model observation",
|
|
38
|
+
"summary": "Attaches the application's existing renderer model owners through its existing Core client.",
|
|
39
|
+
"availability": "JavaScript with an existing Core client and optional existing renderer owners",
|
|
40
|
+
"protocol": "Request-owned model.observation.renderer attachment and complete snapshot publication",
|
|
41
|
+
"normalization": "Returns ready, closed, current() and close(). Ready acknowledges actual attachmentRequestId/coreRequestId; subscriptions publish complete current owner snapshots and reread after acknowledgement. Existing Core serialization encodes owner Error fields at the JSON boundary without changing other fields. Abort, page retirement, exact installed-client retirement, transport failure or explicit close retires only observation RPCs/subscriptions and leaves models/client owned by the caller. Normal cancellation closes cleanly; pre-acknowledgement cancellation rejects ready. Other failures reject public promises and reach onError or complete console diagnostics. No automatic reattachment, owner creation, model work or page-render barrier."
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
"id": "ai-model-observation:subscribeModelObservation",
|
|
45
|
+
"name": "subscribeModelObservation",
|
|
46
|
+
"displayName": "subscribeModelObservation()",
|
|
47
|
+
"kind": "function",
|
|
48
|
+
"signature": "subscribeModelObservation({client,signal,onState,onDiagnostic,onError}={})",
|
|
49
|
+
"entrypoints": ["arcane-os/ai/model-observation"],
|
|
50
|
+
"primaryImport": "arcane-os/ai/model-observation",
|
|
51
|
+
"group": "Existing model observation",
|
|
52
|
+
"summary": "Subscribes to complete existing model-owner snapshots and explicitly selected live Core diagnostics.",
|
|
53
|
+
"availability": "JavaScript with an existing Core client and composed observation service",
|
|
54
|
+
"protocol": "Request-owned model.observation.watch with targeted state and optional error events",
|
|
55
|
+
"normalization": "Requires onState; returns ready, closed, current() and close(). Ready resolves the first current snapshot; onDiagnostic explicitly selects diagnostics:true and receives complete correlated live failed Core response frames, without recovered history or inferred model IDs. Closing, abort, page/client retirement and transport failures retire only observation work. Normal cancellation closes cleanly; other errors reach onError and reject public promises. Callback errors close the observation while callback promises remain caller-owned. No model refresh, inference, polling, host launch, endpoint discovery, reconnect or saved conversation content."
|
|
56
|
+
},
|
|
15
57
|
{
|
|
16
58
|
"id": "core-app-control:connectAppControl",
|
|
17
59
|
"name": "connectAppControl",
|
|
@@ -4374,7 +4416,7 @@
|
|
|
4374
4416
|
"summary": "Creates an app-neutral Core dispatcher with explicit services, request lifetimes, events and graceful drain.",
|
|
4375
4417
|
"availability": "Node on Windows, Linux and macOS native hosts; Android requires its host adaptation",
|
|
4376
4418
|
"protocol": "arcane/1 dispatcher and service lifecycle",
|
|
4377
|
-
"normalization": "Starts services independently; runtime.getService(name) and context.getService(name) return the same registered object after only its shared startup, without a renderer or global barrier. Lookup is readiness, not a lifetime lease; new lookups reject during shutdown. runtime.handle(frame,{contextRequestId})
|
|
4419
|
+
"normalization": "Starts services independently; runtime.getService(name) and context.getService(name) return the same registered object after only its shared startup, without a renderer or global barrier. Lookup is readiness, not a lifetime lease; new lookups reject during shutdown. runtime.handle(frame,{contextRequestId,contextClientRequestId}) sets requestId=contextRequestId??frame.id, clientRequestId=contextClientRequestId??contextRequestId??frame.id and coreRequestId=frame.id; omitted metadata exposes all three equal. Transports always supply the actual client ID separately, preserving the owning shared-host internal requestId contract. Internal IDs continue to own cancellation/state/event envelopes and payloads remain unchanged. Preserves complete results/errors, cancels request-lifetime work and drains accepted service work; runtime.replay publishes current dispatcher/service state without restart and preserves reentrant lifecycle publication order. Missing dispatch retains METHOD_NOT_ALLOWED with exact method/namespace and core-namespace-unavailable or core-method-unavailable; named services, registered methods and built-in namespaces establish presence. Handler/startup/permission errors remain unchanged."
|
|
4378
4420
|
},
|
|
4379
4421
|
{
|
|
4380
4422
|
"id": "core-stdio:encodeCoreFrame",
|
|
@@ -4416,7 +4458,7 @@
|
|
|
4416
4458
|
"summary": "Attaches an existing Core runtime to framed input and output with concurrent dispatch and graceful shutdown.",
|
|
4417
4459
|
"availability": "Node with readable input and writable output streams",
|
|
4418
4460
|
"protocol": "Content-Length framed arcane/1 stdio",
|
|
4419
|
-
"normalization": "Returns {runtime,closed,close}; scopes responses/request events, cancellation and current-state replay to its connection, preserving original service-facing requestId and complete payloads while
|
|
4461
|
+
"normalization": "Returns {runtime,closed,close}; scopes responses/request events, cancellation and current-state replay to its connection, preserving original service-facing requestId/clientRequestId and complete payloads while coreRequestId identifies unique internal dispatch. Preserves queued output order, reports parser and stream failures, drains accepted runtime work on EOF and never exits the host or ends caller-owned output"
|
|
4420
4462
|
},
|
|
4421
4463
|
{
|
|
4422
4464
|
"id": "native-portable-provider:createPortableNativeProvider",
|
|
@@ -4430,7 +4472,7 @@
|
|
|
4430
4472
|
"summary": "Creates the SDK-owned provider that assembles one selected app and explicit services into a portable Core payload.",
|
|
4431
4473
|
"availability": "Node; produces a portable payload rather than an executable host",
|
|
4432
4474
|
"protocol": "arcane-native-builder/1 portable provider",
|
|
4433
|
-
"normalization": "Preserves complete selected app and dependency releases; an explicit services selection overrides appDescriptor.native.services, otherwise the descriptor selection is used; optional appDescriptor.native.window carries positive integral logical client width/height and optional resizable into manifest.window with omitted fields preserved;
|
|
4475
|
+
"normalization": "Preserves complete selected app and dependency releases; an explicit services selection overrides appDescriptor.native.services, otherwise the descriptor selection is used; optional appDescriptor.native.window carries positive integral logical client width/height and optional resizable into manifest.window with omitted fields preserved. Generated coreListener opt-in composes one passive model-observation service from the existing host.runtime and actual services exposing localai.status/image.status/decisions.status before listener binding, with absent owners null and renderer attachment separate. Ordinary stdio and sharedHost/headless behavior remains unchanged; no model creation, getService or readiness barrier. Reports executable:false and never substitutes for an unavailable platform host"
|
|
4434
4476
|
},
|
|
4435
4477
|
{
|
|
4436
4478
|
"id": "native-portable-provider:arcaneNativeBuilderProvider",
|
|
@@ -4640,7 +4682,7 @@
|
|
|
4640
4682
|
"summary": "Attaches local IPC to an existing window-owned Core runtime without changing its services or lifetime.",
|
|
4641
4683
|
"availability": "Node named pipes on Windows and Unix-domain sockets on Linux/macOS; Android requires its host adaptation",
|
|
4642
4684
|
"protocol": "arcane/1 over local IPC with per-connection correlation, cancellation and current-state replay",
|
|
4643
|
-
"normalization": "Returns {endpoint,closed,close}; never constructs, starts or closes the supplied runtime. Explicit close flushes queued output and disconnects its clients while accepted service work remains runtime-owned. Runtime shutdown retains the endpoint through service drain then closes the listener. Preserves parameters/results/event data, original handler requestId and internal coreRequestId; no host shutdown RPC. Bind errors reject startup; transport errors reach onError and terminal errors reject closed. Native launchContext.coreListener:{endpoint} selects the existing stdio branch; no sharedHost/headless selection or inferred endpoint."
|
|
4685
|
+
"normalization": "Returns {endpoint,closed,close}; never constructs, starts or closes the supplied runtime or registers services. Explicit close flushes queued output and disconnects its clients while accepted service work remains runtime-owned. Runtime shutdown retains the endpoint through service drain then closes the listener. Preserves parameters/results/event data, original handler requestId/clientRequestId and internal coreRequestId; no host shutdown RPC. Bind errors reject startup; transport errors reach onError and terminal errors reject closed. Native launchContext.coreListener:{endpoint} selects the existing stdio branch and its existing-instance observation composition; no sharedHost/headless selection or inferred endpoint."
|
|
4644
4686
|
},
|
|
4645
4687
|
{
|
|
4646
4688
|
"id": "core-host:startSharedCoreHost",
|
|
@@ -4654,7 +4696,7 @@
|
|
|
4654
4696
|
"summary": "Claims an app-selected local endpoint before composing one shared Core runtime.",
|
|
4655
4697
|
"availability": "Node named pipes on Windows and Unix-domain sockets on Linux/macOS; Android requires its host adaptation",
|
|
4656
4698
|
"protocol": "arcane/1 over local IPC with per-connection request correlation and current-state replay",
|
|
4657
|
-
"normalization": "Returns {runtime,endpoint,closed,close}; configure(runtime,{signal}) registers independently started services after the endpoint claim. Close joins composition, accepted service work, disposal and queued frames before releasing the endpoint. Complete transport/startup/shutdown errors remain observable; an occupied endpoint never invokes the factory"
|
|
4699
|
+
"normalization": "Returns {runtime,endpoint,closed,close}; configure(runtime,{signal}) registers independently started services after the endpoint claim. Preserves internal handler requestId/coreRequestId and the existing matching data.requestId adapter mapping while additive clientRequestId exposes the actual client ID independently. Other payload fields remain unchanged. Close joins composition, accepted service work, disposal and queued frames before releasing the endpoint. Complete transport/startup/shutdown errors remain observable; an occupied endpoint never invokes the factory"
|
|
4658
4700
|
},
|
|
4659
4701
|
{
|
|
4660
4702
|
"id": "core-host:connectSharedCoreHost",
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
# Existing model observation
|
|
2
|
+
|
|
3
|
+
Model observation reads the existing renderer and Core owners selected by the
|
|
4
|
+
application. It exposes their current lifecycle, progress, retained errors and
|
|
5
|
+
actual operation identities to an explicitly selected developer observer.
|
|
6
|
+
Model loading, inference, selection, cancellation and saved application state
|
|
7
|
+
remain with those owners. Observation creates no model, accessor or Core runtime.
|
|
8
|
+
|
|
9
|
+
## Compose the existing Core
|
|
10
|
+
|
|
11
|
+
```js
|
|
12
|
+
import {createModelObservationService} from 'arcane-os/core/model-observation';
|
|
13
|
+
|
|
14
|
+
const observation = createModelObservationService({
|
|
15
|
+
runtime: host.runtime,
|
|
16
|
+
localAI: existingLocalAIService,
|
|
17
|
+
image: existingImageService,
|
|
18
|
+
decisions: existingDecisionService
|
|
19
|
+
});
|
|
20
|
+
host.runtime.registerService(observation);
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
`host` and the three services in this example are the application's existing
|
|
24
|
+
instances. `localAI`, `image` and `decisions` are optional and default to `null`.
|
|
25
|
+
Supply their actual service definitions, including `current()` and `methods`.
|
|
26
|
+
The constructor neither looks up nor starts a service. Registration can follow
|
|
27
|
+
`startCoreHost()` on its returned runtime, before opening an additional listener;
|
|
28
|
+
it requires no model readiness barrier.
|
|
29
|
+
|
|
30
|
+
The service is named `model-observation` and exposes `current()`, `dispose()` and
|
|
31
|
+
the fixed Core methods below. `dispose()` retires only its observation requests
|
|
32
|
+
and subscriptions. The host continues to own the Core and model services.
|
|
33
|
+
|
|
34
|
+
An external local caller can connect through the explicitly selected
|
|
35
|
+
[`startCoreListener`](core-shared-host.md) endpoint on that same runtime.
|
|
36
|
+
For a packaged window, `native.launchContext.coreListener: {endpoint}` selects
|
|
37
|
+
that same-runtime listener. Before binding it, the generated Core entry registers
|
|
38
|
+
`model-observation` with the actual existing `services` definitions that expose
|
|
39
|
+
`localai.status`, `image.status` and `decisions.status`; absent owners are `null`.
|
|
40
|
+
The observer reuses those model-service owners and creates no accessor or model.
|
|
41
|
+
The application supplies its renderer attachment separately.
|
|
42
|
+
The observation module does not discover endpoints, launch a host, reconnect,
|
|
43
|
+
change a profile, or create a second runtime. Native DOM inspection and actions
|
|
44
|
+
retain their separate [app-control contract](native-app-control.md).
|
|
45
|
+
|
|
46
|
+
## Attach existing renderer owners
|
|
47
|
+
|
|
48
|
+
```js
|
|
49
|
+
import * as aiRuntimeState from 'arcane-os/ai-runtime-state';
|
|
50
|
+
import {attachModelObservation} from 'arcane-os/ai/model-observation';
|
|
51
|
+
|
|
52
|
+
const attachment = attachModelObservation({
|
|
53
|
+
client: existingCoreClient,
|
|
54
|
+
aiRuntimeState,
|
|
55
|
+
imageRuntime: existingImageAccessor,
|
|
56
|
+
decisionModel: existingBrowserDecisionModel,
|
|
57
|
+
modelController: existingModelController,
|
|
58
|
+
signal: pageLifetime.signal,
|
|
59
|
+
onError: function reportObservationFailure(error) {
|
|
60
|
+
developerDiagnostics.report(error);
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
await attachment.ready;
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
Each optional owner defaults to `null`. Pass the instances already used by the
|
|
67
|
+
application. Creating another image accessor would describe that new accessor's
|
|
68
|
+
requests rather than the existing work. The supported owner methods are:
|
|
69
|
+
|
|
70
|
+
| Input | Read | Subscribe |
|
|
71
|
+
|---|---|---|
|
|
72
|
+
| `aiRuntimeState` | `getAIRuntimeState()` | `subscribeAIRuntimeState()` |
|
|
73
|
+
| `imageRuntime` | `current()` | `subscribe()` |
|
|
74
|
+
| `decisionModel` | `status()` | `subscribe()` |
|
|
75
|
+
| `modelController` | `status()` | `on('statechange')`, `on('progress')` |
|
|
76
|
+
|
|
77
|
+
The attachment returns `{ready, closed, current, close}`. `ready` resolves with
|
|
78
|
+
`{attachmentRequestId, coreRequestId}` after Core acknowledges the attachment.
|
|
79
|
+
`current()` returns the latest complete renderer snapshot read by this
|
|
80
|
+
attachment, or `null` when it was cancelled before reading. Subscriptions begin
|
|
81
|
+
after acknowledgement; the attachment then reads and publishes current state
|
|
82
|
+
again to cover changes during that handshake.
|
|
83
|
+
|
|
84
|
+
Caller abort, `pagehide`, retirement of the exact installed Core client,
|
|
85
|
+
transport failure, or `close()` removes the observation subscriptions and
|
|
86
|
+
cancels only this attachment's observation RPCs. A supplied client remains
|
|
87
|
+
caller-owned. The attachment never follows a replacement client or automatically
|
|
88
|
+
reattaches. Native document retirement also cancels its long-lived Core request.
|
|
89
|
+
Core then removes the renderer record; late publication to that retired
|
|
90
|
+
attachment fails honestly. Detachment does not unload or cancel a model.
|
|
91
|
+
|
|
92
|
+
`close()` returns `closed`, which settles after the attachment's observation
|
|
93
|
+
RPC promises settle locally. It does not claim that a remote model or host has
|
|
94
|
+
closed. Transport and observation errors remain available through `onError`
|
|
95
|
+
and the rejected public promises. With `onError` omitted they reach the developer
|
|
96
|
+
console. Normal cancellation, including cancellation initiated by the existing
|
|
97
|
+
client or native document owner, closes observation without a model error. When
|
|
98
|
+
cancellation precedes acknowledgement, `ready` rejects with the cancellation
|
|
99
|
+
while `closed` resolves after the observation RPCs settle.
|
|
100
|
+
|
|
101
|
+
## Read or subscribe from an existing client
|
|
102
|
+
|
|
103
|
+
```js
|
|
104
|
+
import {subscribeModelObservation} from 'arcane-os/ai/model-observation';
|
|
105
|
+
|
|
106
|
+
const current = await client.invoke('model.observation.current', {}, {signal});
|
|
107
|
+
const observation = subscribeModelObservation({
|
|
108
|
+
client,
|
|
109
|
+
signal,
|
|
110
|
+
onState: function showRetainedOwners(snapshot) {
|
|
111
|
+
developerDiagnostics.show(snapshot);
|
|
112
|
+
},
|
|
113
|
+
onDiagnostic: function showModelFailure(record) {
|
|
114
|
+
developerDiagnostics.report(record);
|
|
115
|
+
},
|
|
116
|
+
onError: function showObservationFailure(error) {
|
|
117
|
+
developerDiagnostics.report(error);
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
await observation.ready;
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
`onState` is required. The returned `{ready, closed, current, close}` has the same
|
|
124
|
+
observation-only lifecycle as the renderer attachment. `ready` resolves with
|
|
125
|
+
the first current-state replay; `current()` holds the latest complete snapshot,
|
|
126
|
+
initially `null`. `onDiagnostic` explicitly selects live correlated Core model
|
|
127
|
+
failure frames. Omitting it requests state observation only. Callback failures
|
|
128
|
+
close that observation and reach `onError`; callback promises remain
|
|
129
|
+
caller-owned and may await `close()` without becoming its completion barrier.
|
|
130
|
+
|
|
131
|
+
Every snapshot is:
|
|
132
|
+
|
|
133
|
+
```js
|
|
134
|
+
{
|
|
135
|
+
core: existingCoreRuntime.current(),
|
|
136
|
+
native: {localAI, image, decisions},
|
|
137
|
+
renderers: [{attachmentRequestId, coreRequestId, owners: {
|
|
138
|
+
aiRuntimeState, imageRuntime, decisionModel, modelController
|
|
139
|
+
}}]
|
|
140
|
+
}
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
The values represent complete supplied-owner snapshots. Absent optional owners
|
|
144
|
+
are `null`; absent renderer attachments produce an empty array. Core state also
|
|
145
|
+
contains its real active observation requests, identified by their actual
|
|
146
|
+
methods, alongside other active requests. Observation does not relabel them as
|
|
147
|
+
model work. Snapshots are current observations of independent owners, rather
|
|
148
|
+
than an atomic transaction across all owners.
|
|
149
|
+
|
|
150
|
+
## Fixed transport and correlation
|
|
151
|
+
|
|
152
|
+
| Method/event | Contract |
|
|
153
|
+
|---|---|
|
|
154
|
+
| `model.observation.current` | Empty parameters; returns the current snapshot without model refresh or readiness waits. |
|
|
155
|
+
| `model.observation.watch` | `{diagnostics:false}` by default; remains pending until cancellation. |
|
|
156
|
+
| `model.observation.state` | Targeted watch event `{watchRequestId,snapshot}`; the first event acknowledges the watch and replays current state. |
|
|
157
|
+
| `model.observation.error` | Targeted diagnostic `{watchRequestId,source:'core',method,frame}` preserving the complete original failed response frame. |
|
|
158
|
+
| `model.observation.renderer.attach` | `{snapshot}`; remains pending to own the renderer publication lifetime. |
|
|
159
|
+
| `model.observation.renderer.attached` | Targeted acknowledgement `{attachmentRequestId,coreRequestId}`. |
|
|
160
|
+
| `model.observation.renderer.publish` | `{attachmentRequestId,coreRequestId,snapshot}`; replaces that attachment's current snapshot and returns both attachment identities. |
|
|
161
|
+
|
|
162
|
+
Both helpers register listeners before invoking. Existing
|
|
163
|
+
[`client.invoke`](core-client.md) supplies `onRequest({requestId})` before send;
|
|
164
|
+
long-lived observation requests use `timeoutMs:0`. Their acknowledgement is a
|
|
165
|
+
targeted request event, while the invocation promise remains pending.
|
|
166
|
+
|
|
167
|
+
`attachmentRequestId` and `watchRequestId` are the actual client-visible Core
|
|
168
|
+
request identities supplied by service context `clientRequestId`.
|
|
169
|
+
`coreRequestId` is the actual internal Core frame identity;
|
|
170
|
+
renderer attachments are keyed by that value so clients with identical local
|
|
171
|
+
request IDs cannot collide. The renderer echoes both values when publishing.
|
|
172
|
+
`snapshot.core.activeRequests[].id` and diagnostic `frame.id` use internal Core
|
|
173
|
+
identities. They can differ from client-visible IDs under connection routing.
|
|
174
|
+
None of these observation/transport IDs is a model-operation ID.
|
|
175
|
+
|
|
176
|
+
Core supplies both metadata fields for direct runtime calls, stdio connections,
|
|
177
|
+
`startCoreListener()` connections and the existing `startSharedCoreHost()` path.
|
|
178
|
+
Each connection preserves the original incoming request ID as `clientRequestId`
|
|
179
|
+
independently of its established `requestId` mapping. Direct calls use the
|
|
180
|
+
incoming frame ID for both identities. Observation requires these actual
|
|
181
|
+
context fields; it does not infer them from `requestId` or rewrite event
|
|
182
|
+
payloads to manufacture correlation.
|
|
183
|
+
|
|
184
|
+
The exact `methods` keys on the supplied native service definitions select the
|
|
185
|
+
Core operations whose live failures can be observed. The observer tracks their
|
|
186
|
+
actual active request IDs while a watch exists. Core publishes request removal
|
|
187
|
+
before emitting its response; the observer retains that correlation until the
|
|
188
|
+
response arrives, then removes it. Removing the final watcher releases frame
|
|
189
|
+
observation and remaining transient correlation. It retains no failure history.
|
|
190
|
+
|
|
191
|
+
## Evidence and lifecycle limits
|
|
192
|
+
|
|
193
|
+
- AIRuntimeState exposes its existing llm/stt/tts records, including the reported
|
|
194
|
+
operation ID. Its retained errors contain only code/message. Concurrent role
|
|
195
|
+
work is not a complete request enumeration in this snapshot.
|
|
196
|
+
- The existing image accessor exposes its own active method, stream ID, status
|
|
197
|
+
and progress records. Its retained error is preserved; that error has no
|
|
198
|
+
reliable explicit stream association. Constructor-only diagnostic callbacks
|
|
199
|
+
cannot be retroactively attached through this API.
|
|
200
|
+
- Browser and native decision snapshots retain their existing errors and active
|
|
201
|
+
request counts. A count does not supply missing per-evaluation IDs.
|
|
202
|
+
- ModelController status exposes its existing state/progress and code/message
|
|
203
|
+
error. Its load/unload event IDs are not retained by `status()`, and are not
|
|
204
|
+
presented as inference IDs or reconstructed history here.
|
|
205
|
+
- Core supplies its real active request identities and complete live failed
|
|
206
|
+
response frames. A watch cannot recover responses that settled before it
|
|
207
|
+
observed them. Retained owner errors remain available in current snapshots.
|
|
208
|
+
|
|
209
|
+
Renderer `Error` values in owner error fields use the existing Core error
|
|
210
|
+
serializer at the JSON boundary; other owner fields remain unchanged. A value
|
|
211
|
+
the selected transport cannot encode fails through the observation error path
|
|
212
|
+
rather than being replaced with shortened or invented content. Observation
|
|
213
|
+
does not alter model inputs, outputs, prompts or their persistence policy.
|
|
214
|
+
|
|
215
|
+
The shared JavaScript contract is portable wherever the existing Core client
|
|
216
|
+
and supplied owners are available. Endpoint and native-host availability belong
|
|
217
|
+
to their documented adapters. Developer observation remains separate from
|
|
218
|
+
ordinary user status, app-owned stage/avatar association and actual model
|
|
219
|
+
execution verification. This capability does not establish that an inference
|
|
220
|
+
failure has been corrected.
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
`connectAppControl` from `arcane-os/core/app-control` connects to one explicitly
|
|
4
4
|
selected running application window. The Windows WebView2 host provides document
|
|
5
|
-
inspection, a PNG of its rendered viewport,
|
|
6
|
-
own local named pipe. The application
|
|
7
|
-
origin and window lifecycle. These operations do not activate the desktop window
|
|
5
|
+
inspection, a PNG of its rendered viewport, targeted DOM actions, fixed keys and
|
|
6
|
+
Normal-window client resizing through its own local named pipe. The application
|
|
7
|
+
keeps its existing Core child, profile, origin and window lifecycle. These operations do not activate the desktop window
|
|
8
8
|
or send global keyboard or pointer input.
|
|
9
9
|
|
|
10
10
|
The Node client uses the SDK's existing `arcane/1` framing and local pipe/socket
|
|
@@ -77,29 +77,45 @@ connection or other business result.
|
|
|
77
77
|
| `inspect(parameters={}, options?)` | Optional `shadowPath` selects an open shadow root. Optional CSS `selector` selects zero or more elements within the selected root; omission selects the whole root. Optional `documentGeneration` selects the expected document. Returns complete HTML/text, live control state and DOM-derived role/name hints. |
|
|
78
78
|
| `capture(parameters={}, options?)` | Returns `{mimeType:'image/png', data, documentGeneration, url}`. `data` contains the complete base64 PNG. Optional `documentGeneration` selects the expected document. |
|
|
79
79
|
| `act(parameters, options?)` | Requires `documentGeneration`, one unambiguous CSS `selector` within the root selected by optional `shadowPath`, and the action fields below. |
|
|
80
|
+
| `key(parameters, options?)` | Requires `documentGeneration` and `key:'Tab'`, `'Enter'`, or `'Space'`. Optional `shiftKey:true` is supported only for Tab. Sends one ordered press/release pair to the selected WebView's current focus. |
|
|
81
|
+
| `resize(parameters, options?)` | Requires positive integral native client `width` and `height` supported by WinForms. Optional `documentGeneration` selects the expected document. Resizes only a Normal window and returns immediate previous/actual client dimensions plus the subsequently observed CSS viewport. |
|
|
80
82
|
| `close()` | Closes this caller's connection and cancels its pending requests. It never closes the app or Core. Returns the connection's completion promise. |
|
|
81
83
|
| `closed` | Connection lifetime promise; actual transport failures remain observable. |
|
|
82
84
|
| `endpoint` | The caller-selected local endpoint. |
|
|
83
85
|
|
|
84
86
|
Each operation's separate `options` uses the existing Core client request
|
|
85
87
|
contract, including `signal` and `timeoutMs`. Connection cancellation closes only
|
|
86
|
-
that connection. Cancellation before UI dispatch prevents execution
|
|
87
|
-
|
|
88
|
-
the
|
|
89
|
-
|
|
88
|
+
that connection. Cancellation before UI dispatch prevents execution; keys also
|
|
89
|
+
check cancellation after waiting for the keyboard owner and immediately before
|
|
90
|
+
the press. Once a script, capture, key press or resize has been dispatched,
|
|
91
|
+
cancellation cannot reverse its effects. The host observes its actual completion,
|
|
92
|
+
including the owned key release. The caller receives its ordinary abort result;
|
|
93
|
+
the complete later response is retained in host diagnostics for a cancelled
|
|
94
|
+
request or closed connection instead of being represented as delivered.
|
|
90
95
|
|
|
91
96
|
## Document operations
|
|
92
97
|
|
|
93
98
|
Inspection defaults to the top-level light DOM. Its result contains `scope`, `title`,
|
|
94
99
|
`readyState`, `doctype`, `roots`, `controls`, `frames`, `shadowHosts`,
|
|
95
|
-
`activeElement`, `scrollingElement`, `selection`, `
|
|
96
|
-
`url`. Each root retains its complete
|
|
100
|
+
`activeElement`, `scrollingElement`, `selection`, `viewport`, `documentHasFocus`,
|
|
101
|
+
`focusPath`, `documentGeneration`, and `url`. Each root retains its complete
|
|
102
|
+
`html` and `text`; controls retain their
|
|
97
103
|
attributes and current form, selection, disabled, scrolling and related state.
|
|
98
104
|
Generated CSS selectors are useful for the inspected document; a later DOM edit
|
|
99
105
|
can change their meaning. Application-authored stable selectors are preferable
|
|
100
106
|
when available. Component replacement or mutation can make a selector stale
|
|
101
107
|
without changing the native document generation.
|
|
102
108
|
|
|
109
|
+
`viewport` contains the top-level document's `innerWidth` and `innerHeight` as
|
|
110
|
+
CSS-pixel `width`/`height`, plus its current `devicePixelRatio`.
|
|
111
|
+
`documentHasFocus` records `document.hasFocus()`. `focusPath` describes the
|
|
112
|
+
top-level active element followed by each active element in accessible open
|
|
113
|
+
shadow roots along that focus branch. These records retain complete attributes,
|
|
114
|
+
DOM-derived names and control state, with `selector:null`; they are observations,
|
|
115
|
+
not selector targets. Reading a focused body or host's name can traverse its
|
|
116
|
+
complete text subtree. The path stops at a frame element or closed shadow host;
|
|
117
|
+
it does not enter frame documents or closed roots.
|
|
118
|
+
|
|
103
119
|
### Open shadow roots
|
|
104
120
|
|
|
105
121
|
`inspect` and `act` accept `shadowPath`, an ordered array of CSS host selectors.
|
|
@@ -134,7 +150,10 @@ inspection; pair selectors from its result with that same path for actions.
|
|
|
134
150
|
`activeElement` comes from the selected root. `scrollingElement` is `null` for a
|
|
135
151
|
shadow-root scope because the root has no document scrolling element; scroll
|
|
136
152
|
actions still target elements. `selection.text`, `title`, `readyState`, `doctype`
|
|
137
|
-
and native URL/generation remain document-level information.
|
|
153
|
+
and native URL/generation remain document-level information. The added
|
|
154
|
+
`viewport`, `documentHasFocus` and `focusPath` also always describe the top-level
|
|
155
|
+
document: the focus path can enter open roots outside the selected inspection
|
|
156
|
+
root. The selected root's `activeElement` remains separate. Role/name label
|
|
138
157
|
references resolve within the element's own tree. Inspection follows the DOM
|
|
139
158
|
tree, not the composed rendering tree: slot-assigned light-DOM nodes remain in
|
|
140
159
|
their own containing DOM tree and are not duplicated inside the selected shadow
|
|
@@ -155,9 +174,10 @@ actual target state after dispatch, including disconnection by the action.
|
|
|
155
174
|
|
|
156
175
|
Role/name hints are derived from DOM attributes and labels, not a native
|
|
157
176
|
accessibility-tree computation. Frame documents and unselected shadow-root
|
|
158
|
-
contents are outside the selected scope
|
|
159
|
-
|
|
160
|
-
|
|
177
|
+
contents are outside the selected HTML/control scope; the document-level focus
|
|
178
|
+
path separately describes its active open-shadow branch. Frame elements and
|
|
179
|
+
open-shadow hosts are described; closed shadow roots cannot be discovered.
|
|
180
|
+
Capture is the current rendered viewport, not an offscreen full-document rendering. Covered and minimized-window
|
|
161
181
|
capture behavior requires execution evidence in the selected environment.
|
|
162
182
|
|
|
163
183
|
| Action | Parameters and immediate behavior |
|
|
@@ -174,11 +194,101 @@ rewritten by the SDK.
|
|
|
174
194
|
|
|
175
195
|
The host publishes a new generation when an actual replacement document becomes
|
|
176
196
|
ready. A creation-time document marker is also checked inside the executing
|
|
177
|
-
script so a queued action cannot run against a successor page. During navigation,
|
|
197
|
+
script so a queued DOM action cannot run against a successor page. During navigation,
|
|
178
198
|
operations report that the document is not ready. A change during completion
|
|
179
199
|
reports the actual result with that failure; actions are never retried. These
|
|
180
200
|
markers are transient document lifecycle state and are not written to saved data.
|
|
181
201
|
|
|
202
|
+
## Fixed keys
|
|
203
|
+
|
|
204
|
+
```js
|
|
205
|
+
const page = await app.inspect();
|
|
206
|
+
const moved = await app.key({documentGeneration: page.documentGeneration, key: 'Tab'});
|
|
207
|
+
console.log(moved.previous.focusPath, moved.actual.focusPath);
|
|
208
|
+
await app.key({documentGeneration: moved.documentGeneration, key: 'Tab', shiftKey: true});
|
|
209
|
+
await app.key({documentGeneration: moved.documentGeneration, key: 'Enter'});
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
Keys use the selected WebView's current focus. No selector, shadow path, frame
|
|
213
|
+
selection, focus assignment or desktop activation is performed. Tab and
|
|
214
|
+
Shift+Tab use a fixed Tab `rawKeyDown`/`keyUp` pair, with the Shift modifier on
|
|
215
|
+
both events for Shift+Tab. Enter and Space use `keyDown` with their corresponding
|
|
216
|
+
text, followed by `keyUp`, preserving browser default actions and application
|
|
217
|
+
`preventDefault` handling. Shift+Tab does not send a separate Shift key pair.
|
|
218
|
+
The API exposes these fixed keys, not an arbitrary protocol method or script.
|
|
219
|
+
|
|
220
|
+
The result contains `key`, `shiftKey`, `previous` and `actual` document views,
|
|
221
|
+
`press`, `release`, `documentGeneration` and `url`. Each view contains the same
|
|
222
|
+
`viewport`, `documentHasFocus` and `focusPath` described above. Each key phase
|
|
223
|
+
records its fixed `parameters`, `attempted`, `completed`, and the complete
|
|
224
|
+
protocol `response` when returned. Dispatch completion describes the native
|
|
225
|
+
command, not application handler completion or a successful business action.
|
|
226
|
+
Inspect the resulting page to establish the application outcome.
|
|
227
|
+
|
|
228
|
+
Accepted key pairs share one keyboard owner so another key request cannot
|
|
229
|
+
interleave between press and release. Other independent control operations keep
|
|
230
|
+
their existing scheduling. The host awaits each native phase in order. Once
|
|
231
|
+
the press is attempted, the host observes that task and attempts the matching
|
|
232
|
+
release on the same WebView even after cancellation, navigation or press
|
|
233
|
+
failure. Release can itself activate Space. A failed or ambiguous press is
|
|
234
|
+
never replayed.
|
|
235
|
+
|
|
236
|
+
Generation checks occur before dispatch and after completion; the native input
|
|
237
|
+
command has no atomic document binding. Navigation or focus changes can occur
|
|
238
|
+
between the checks and phases, and Tab intentionally changes focus before its
|
|
239
|
+
release. A detected document change is reported with the actual attempted and
|
|
240
|
+
completed phases. Dispatch, release or later observation failures retain the
|
|
241
|
+
complete partial result at `error.data.find(entry => entry.key === 'result').value`
|
|
242
|
+
under `ARCANE_APP_CONTROL_FAILED`. Original exceptions remain in `cause`; if
|
|
243
|
+
both native phases fail, both errors are also retained in the aggregate and
|
|
244
|
+
their respective phase records. An unavailable `actual` view is omitted rather
|
|
245
|
+
than inferred.
|
|
246
|
+
|
|
247
|
+
## Resize and restore a Normal window
|
|
248
|
+
|
|
249
|
+
```js
|
|
250
|
+
const resized = await app.resize({width: 1200, height: 800});
|
|
251
|
+
try {
|
|
252
|
+
console.log(resized.previous, resized.actual, resized.viewport);
|
|
253
|
+
console.log(await app.inspect({selector: '#pm-content'}));
|
|
254
|
+
} finally {
|
|
255
|
+
await app.resize({width: resized.previous.width, height: resized.previous.height});
|
|
256
|
+
}
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
`width` and `height` select the native WinForms client dimensions, in the same
|
|
260
|
+
units returned by `status().window`. They are not CSS viewport dimensions.
|
|
261
|
+
The host changes only `ClientSize`; it does not assign window state, position,
|
|
262
|
+
restore bounds, activation or WebView zoom, and it preserves the profile and
|
|
263
|
+
origin. Read `viewport` for the resulting CSS dimensions instead of assuming a
|
|
264
|
+
particular DPI or zoom conversion.
|
|
265
|
+
|
|
266
|
+
Immediately before the UI-thread mutation, the host checks the actual
|
|
267
|
+
`WindowState` and records `previous:{title,state,width,height}`. It records
|
|
268
|
+
`actual` in the same shape immediately after the native setter, then observes
|
|
269
|
+
the CSS `viewport` asynchronously. The result also contains `requested`,
|
|
270
|
+
`resizeAttempted`, `resizeCompleted`, `documentGeneration` and `url`.
|
|
271
|
+
Platform constraints may make actual dimensions differ from requested ones.
|
|
272
|
+
Other input or concurrent resize requests can change the window between the
|
|
273
|
+
native snapshot and the document observation; these records are not an atomic
|
|
274
|
+
native/CSS snapshot. Completion does not establish that asynchronous application
|
|
275
|
+
resize handlers have settled.
|
|
276
|
+
|
|
277
|
+
Maximized or minimized windows return `ARCANE_APP_CONTROL_FAILED` before any
|
|
278
|
+
size mutation, with `reason:'unsupported-window-state'`,
|
|
279
|
+
`supportedWindowState:'Normal'` and the complete current `window` in the error's
|
|
280
|
+
`data` entries. The host never automatically restores, maximizes or minimizes a
|
|
281
|
+
window to satisfy this request. Invalid dimensions return `INVALID_ARGUMENT`.
|
|
282
|
+
The same state rule applies to explicit restoration.
|
|
283
|
+
|
|
284
|
+
Restore using the actual `previous.width` and `previous.height` from the
|
|
285
|
+
operation being reversed, never a historical status snapshot or CSS viewport
|
|
286
|
+
dimensions. Restoration is an explicit second request; cancellation does not
|
|
287
|
+
resize the window back. If a dispatched resize or its later observation fails,
|
|
288
|
+
the complete partial `result` remains in the error's `data`, including the
|
|
289
|
+
recorded previous dimensions, actual dimensions and attempted/completed flags.
|
|
290
|
+
Use that actual record when deciding whether and how to restore.
|
|
291
|
+
|
|
182
292
|
## CLI
|
|
183
293
|
|
|
184
294
|
```powershell
|
|
@@ -187,14 +297,20 @@ arcane app-control inspect --endpoint '\\.\pipe\arcane-pm-control' --selector '#
|
|
|
187
297
|
arcane app-control inspect --endpoint '\\.\pipe\arcane-pm-control' --request '.arcane\inspection.json'
|
|
188
298
|
arcane app-control capture --endpoint '\\.\pipe\arcane-pm-control' --output '.arcane\view.png'
|
|
189
299
|
arcane app-control act --endpoint '\\.\pipe\arcane-pm-control' --request '.arcane\action.json'
|
|
300
|
+
arcane app-control key --endpoint '\\.\pipe\arcane-pm-control' --request '.arcane\key.json'
|
|
301
|
+
arcane app-control resize --endpoint '\\.\pipe\arcane-pm-control' --request '.arcane\resize.json'
|
|
190
302
|
```
|
|
191
303
|
|
|
192
|
-
The action file is the complete
|
|
304
|
+
The action or key file is the complete parameter object, including the actual
|
|
193
305
|
`documentGeneration` returned by the preceding status or inspection. Inspection
|
|
194
306
|
also accepts `--request` for a complete parameter object, such as
|
|
195
307
|
`{"shadowPath":["#appearance-panel","theme-switcher"]}` or the same object with
|
|
196
308
|
`selector` and `documentGeneration`. Inspection's `--request` and `--selector`
|
|
197
|
-
options are mutually exclusive.
|
|
309
|
+
options are mutually exclusive. A key file can contain
|
|
310
|
+
`{"documentGeneration":3,"key":"Tab","shiftKey":true}`, using the observed
|
|
311
|
+
generation. A resize file contains native dimensions such as
|
|
312
|
+
`{"width":1200,"height":800}`. Each requires `--request`; the CLI reads the
|
|
313
|
+
selected file completely and forwards the parsed parameter object unchanged.
|
|
198
314
|
Paths are relative to the current directory. Capture
|
|
199
315
|
writes the PNG to the explicitly selected file and prints its metadata. The other
|
|
200
316
|
operations print complete JSON. `app-control --help` describes this command's
|