@skrr-ai/cli 0.1.88 → 0.1.89

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.
@@ -36,6 +36,22 @@ export const COMPUTER_EVENTS = {
36
36
  attach: 'computer:attach',
37
37
  /** client → API: leave. */
38
38
  detach: 'computer:detach',
39
+ /**
40
+ * client → API: read the whole manifest again ON THE SAME ATTACHMENT, after
41
+ * a missed delta or a `stale_epoch` (OSK-13815). The ack carries what an
42
+ * attach's does — the attachment id (unchanged), the manifest and the
43
+ * operations — or a typed refusal; `not_found` means the attachment is gone
44
+ * and only then does the client attach afresh. Re-reading state must never
45
+ * change who the reader is: a control lease is bound to the attachment
46
+ * (`holderAttachmentId`), and a resync that detached and attached again
47
+ * orphaned the hold the person had just taken. Answered by the API, which
48
+ * renews the binding with the daemon exactly as its periodic re-check does
49
+ * (a repeated `daemon:computer:attach` with the same id), so it has no
50
+ * daemon mirror and needs no daemon capability. An API that predates it
51
+ * never answers; the client bounds the wait and falls back to attaching
52
+ * afresh.
53
+ */
54
+ resync: 'computer:resync',
39
55
  /** API → client: this attachment was detached, with the reason (revoked, stale generation, …). */
40
56
  detached: 'computer:detached',
41
57
  /** API → client: a whole manifest, or a revisioned delta. */
@@ -177,6 +193,24 @@ export const COMPUTER_SERVER_EVENT_KEYS = [
177
193
  'activity',
178
194
  'filesChanged',
179
195
  ];
196
+ /**
197
+ * Client → API requests the API answers itself, with no `daemon:computer:*`
198
+ * mirror: every other client request is relayed to the daemon under the same
199
+ * key. `resync` renews the binding through the daemon's existing attach
200
+ * (OSK-13815).
201
+ */
202
+ export const COMPUTER_API_ANSWERED_EVENT_KEYS = [
203
+ 'resync',
204
+ ];
205
+ /** How long the client waits for a `computer:resync` answer before attaching afresh (an API that predates it never answers). */
206
+ export const COMPUTER_RESYNC_TIMEOUT_MS = 12_000;
207
+ /**
208
+ * How long a client holds a manifest delta that arrived AHEAD of its
209
+ * predecessor before reading the hole as a loss (OSK-13815). Deltas fan out
210
+ * through a relay of several replicas; a reorder is milliseconds, a loss is
211
+ * forever, and only a loss needs the whole manifest again.
212
+ */
213
+ export const COMPUTER_MANIFEST_REORDER_WAIT_MS = 300;
180
214
  // ---------------------------------------------------------------------------
181
215
  // Capabilities (§7.1)
182
216
  // ---------------------------------------------------------------------------
@@ -281,6 +315,20 @@ export const COMPUTER_CAPABILITIES = {
281
315
  * browser; nothing is proxied to it. Announced by the relay that serves it.
282
316
  */
283
317
  declaredServices: 'computer_declared_services_v1',
318
+ /**
319
+ * The page follows the controlling window (§6.2 "a stable viewport", §7.1,
320
+ * OSK-13808): a browser `viewport` input, sent only by the attachment that
321
+ * holds the browser's lease at the current epoch, sizes the streamed page
322
+ * to that window's content box (`COMPUTER_BROWSER_VIEWPORT_LIMITS`). Only
323
+ * while a person holds control: handing back — or the hold lapsing, being
324
+ * revoked or moving to the agent any other way — restores the browser's own
325
+ * viewport before the agent can act, so an agent never inherits a person's
326
+ * size. Watchers keep seeing the page at whatever size it is, scaled to
327
+ * fit. Announced only where the browser driver can override and restore
328
+ * the page's metrics; without it the window scales the fixed page, as
329
+ * before, and never sends the input.
330
+ */
331
+ browserViewport: 'computer_browser_viewport_v1',
284
332
  };
285
333
  export const COMPUTER_CAPABILITY_VALUES = Object.values(COMPUTER_CAPABILITIES);
286
334
  // ---------------------------------------------------------------------------
@@ -322,6 +370,12 @@ export const COMPUTER_REFUSAL_CODES = [
322
370
  'machine_unavailable',
323
371
  /** The machine's daemon is not answering. */
324
372
  'daemon_unreachable',
373
+ /**
374
+ * The relay failed while answering — an exception, not a fact about the
375
+ * machine. Retryable; the API log carries the same `correlationId`
376
+ * (OSK-13823). Never `machine_unavailable`, which says the machine is down.
377
+ */
378
+ 'relay_error',
325
379
  /** A bounded wait ran out (§7.2 R4). */
326
380
  'timeout',
327
381
  'invalid_request',
@@ -335,22 +389,16 @@ export const COMPUTER_REFUSAL_CODES = [
335
389
  'service_limit',
336
390
  ];
337
391
  /**
338
- * Older spellings the daemon boundary accepts for one release (§5.2). After
339
- * that release this map is emptied, and nothing else names the old spelling.
392
+ * The code when it is a computer refusal, else `null`. There is one spelling
393
+ * per code: the browser's older spelling of `human_control` was accepted for
394
+ * one release and retired with the old browser events (§5.2, OSK-13509).
340
395
  */
341
- export const COMPUTER_LEGACY_REFUSAL_SPELLINGS = Object.freeze({
342
- live_view_active: 'human_control',
343
- });
344
- /** The canonical code for a received spelling, or `null` when it is not a computer refusal. */
345
396
  export function normalizeComputerRefusalCode(code) {
346
397
  if (typeof code !== 'string') {
347
398
  return null;
348
399
  }
349
- if (COMPUTER_REFUSAL_CODES.includes(code)) {
350
- return code;
351
- }
352
- return Object.prototype.hasOwnProperty.call(COMPUTER_LEGACY_REFUSAL_SPELLINGS, code)
353
- ? COMPUTER_LEGACY_REFUSAL_SPELLINGS[code]
400
+ return COMPUTER_REFUSAL_CODES.includes(code)
401
+ ? code
354
402
  : null;
355
403
  }
356
404
  // ---------------------------------------------------------------------------
@@ -365,6 +413,25 @@ export const COMPUTER_ACTOR_KINDS = ['agent', 'human'];
365
413
  * API and a client saw a `seq` hole no fill could close.
366
414
  */
367
415
  export const COMPUTER_ACTIVITY_ACTOR_KINDS = [...COMPUTER_ACTOR_KINDS, 'system'];
416
+ /**
417
+ * The session ids the PLATFORM's own routes run daemon tools under — the web
418
+ * Files surface (`computer-files:` on a personal machine, `dedicated-runtime:`
419
+ * on a Dedicated Runtime) and the owner's `skrr machines dedicated` commands.
420
+ * No agent and no model is behind such a session: the person who asked is
421
+ * known to the API (which audits it), not to the daemon. So an activity event
422
+ * produced under one names no agent (OSK-13790: a person's upload used to read
423
+ * "agent created …"). Built by `ComputerFileService.runPersonalMachineTool`
424
+ * and `DedicatedRuntimeMachineAccess.runDedicatedRuntimeTool`.
425
+ */
426
+ export const COMPUTER_PLATFORM_ROUTE_SESSION_PREFIXES = [
427
+ 'dedicated-runtime:',
428
+ 'computer-files:',
429
+ ];
430
+ /** Whether a tool call's session is one of the platform's own routes, not an agent's. */
431
+ export function isComputerPlatformRouteSession(sessionId) {
432
+ return (typeof sessionId === 'string' &&
433
+ COMPUTER_PLATFORM_ROUTE_SESSION_PREFIXES.some((prefix) => sessionId.startsWith(prefix)));
434
+ }
368
435
  /**
369
436
  * How a person present on the machine reached it: as its owner, or through a
370
437
  * grant at one of the two tiers. Agents carry no access level — an agent
@@ -533,7 +600,11 @@ export const COMPUTER_SURFACE_END_REASONS = [
533
600
  /** Someone signed the computer's browser out everywhere, which ends every browser window. */
534
601
  'browser_reset',
535
602
  ];
536
- /** The browser's fixed logical viewport, whoever is looking (§6.2). */
603
+ /**
604
+ * The browser's logical viewport whoever is looking (§6.2): what the agent
605
+ * always works at. A person holding control may size the page to their window
606
+ * (`computer_browser_viewport_v1`); handing back restores this.
607
+ */
537
608
  export const COMPUTER_BROWSER_VIEWPORT = Object.freeze({ width: 1280, height: 800 });
538
609
  /** One frame never exceeds this, so it fits the SSE transport every guest uses (§7.2 R7). */
539
610
  export const COMPUTER_FRAME_MAX_BYTES = 1024 * 1024;
@@ -607,7 +678,10 @@ export const COMPUTER_TERMINAL_HISTORY_CAUSES = [
607
678
  * verified (OSK-13535).
608
679
  */
609
680
  export const COMPUTER_TERMINAL_CAVEATS = ['windows_pty_unverified'];
610
- /** Input a browser surface takes. `tab` needs `computer_browser_tabs_v1`. */
681
+ /**
682
+ * Input a browser surface takes. `tab` needs `computer_browser_tabs_v1`;
683
+ * `viewport` needs `computer_browser_viewport_v1`.
684
+ */
611
685
  export const COMPUTER_BROWSER_INPUT_TYPES = [
612
686
  'pointer',
613
687
  'key',
@@ -615,7 +689,57 @@ export const COMPUTER_BROWSER_INPUT_TYPES = [
615
689
  'navigate',
616
690
  'history',
617
691
  'tab',
692
+ 'viewport',
618
693
  ];
694
+ /**
695
+ * What a `viewport` input may ask of the page (OSK-13808), in CSS pixels: the
696
+ * controlling window's content box, bounded so no window can make the page
697
+ * absurd for the agent that gets it back, and aspect-clamped (`minAspect` and
698
+ * `maxAspect` are width ÷ height) so a sliver of a window cannot either.
699
+ * Device pixel ratio is not part of it: the page keeps the browser's own, so a
700
+ * high-density screen changes no layout and no picture size.
701
+ */
702
+ export const COMPUTER_BROWSER_VIEWPORT_LIMITS = Object.freeze({
703
+ minWidth: 320,
704
+ maxWidth: 2560,
705
+ minHeight: 240,
706
+ maxHeight: 1600,
707
+ minAspect: 0.4,
708
+ maxAspect: 3.2,
709
+ });
710
+ /**
711
+ * The size a `viewport` input asks for, made legal: whole CSS pixels inside
712
+ * `COMPUTER_BROWSER_VIEWPORT_LIMITS`, its aspect inside the clamp. Total and
713
+ * idempotent: the window sends what this returns and the daemon applies what
714
+ * this returns, so both ends agree on the page a size produces. Null for a size
715
+ * with no area (a hidden or collapsed window asks for nothing).
716
+ */
717
+ export function clampComputerBrowserViewport(size) {
718
+ const { width: rawWidth, height: rawHeight } = size ?? {};
719
+ if (!Number.isFinite(rawWidth) || !Number.isFinite(rawHeight))
720
+ return null;
721
+ if (!(rawWidth > 0) || !(rawHeight > 0))
722
+ return null;
723
+ const limits = COMPUTER_BROWSER_VIEWPORT_LIMITS;
724
+ const bound = (value, low, high) => Math.max(low, Math.min(high, Math.round(value)));
725
+ let width = bound(rawWidth, limits.minWidth, limits.maxWidth);
726
+ let height = bound(rawHeight, limits.minHeight, limits.maxHeight);
727
+ if (width / height > limits.maxAspect) {
728
+ // Too wide: narrow it first; a height already at its floor cannot grow the other way.
729
+ width = Math.max(limits.minWidth, Math.floor(height * limits.maxAspect));
730
+ if (width / height > limits.maxAspect) {
731
+ height = Math.min(limits.maxHeight, Math.ceil(width / limits.maxAspect));
732
+ }
733
+ }
734
+ else if (width / height < limits.minAspect) {
735
+ // Too tall: shorten it first, then widen if the width is at its floor.
736
+ height = Math.max(limits.minHeight, Math.floor(width / limits.minAspect));
737
+ if (width / height < limits.minAspect) {
738
+ width = Math.min(limits.maxWidth, Math.ceil(height * limits.minAspect));
739
+ }
740
+ }
741
+ return { width, height };
742
+ }
619
743
  /**
620
744
  * The client OS a browser `key` input comes from. A person's shortcuts are
621
745
  * mapped onto the Linux page's by the daemon: on `mac`, Command is Control,
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@skrr-ai/auth-core",
3
- "version": "0.4.14",
3
+ "version": "0.4.15",
4
4
  "main": "dist/cjs/index.js",
5
5
  "types": "dist/esm/index.d.ts",
6
6
  "exports": {