@anionex/dsh-computer-use 0.3.1 → 0.3.3

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.
Files changed (63) hide show
  1. package/CHANGELOG.md +130 -0
  2. package/README.md +4 -1
  3. package/README.zh.md +4 -1
  4. package/lib/client.js +1 -1
  5. package/lib/client.js.map +1 -1
  6. package/lib/config.js +49 -20
  7. package/lib/config.js.map +1 -1
  8. package/lib/confirmations.js.map +1 -1
  9. package/lib/exposure.js +19 -6
  10. package/lib/exposure.js.map +1 -1
  11. package/lib/index.js +5 -1
  12. package/lib/index.js.map +1 -1
  13. package/lib/leases.js +4 -1
  14. package/lib/leases.js.map +1 -1
  15. package/lib/providers/macos.js +27 -11
  16. package/lib/providers/macos.js.map +1 -1
  17. package/lib/skill.js +13 -2
  18. package/lib/skill.js.map +1 -1
  19. package/lib/tools.js +1 -1
  20. package/lib/tools.js.map +1 -1
  21. package/lib/types/client/index.d.ts +1 -1
  22. package/lib/types/client/index.d.ts.map +1 -1
  23. package/lib/types/config.d.ts +15 -2
  24. package/lib/types/config.d.ts.map +1 -1
  25. package/lib/types/confirmations.d.ts +1 -1
  26. package/lib/types/confirmations.d.ts.map +1 -1
  27. package/lib/types/exposure.d.ts.map +1 -1
  28. package/lib/types/index.d.ts.map +1 -1
  29. package/lib/types/leases.d.ts +2 -1
  30. package/lib/types/leases.d.ts.map +1 -1
  31. package/lib/types/providers/macos.d.ts +0 -1
  32. package/lib/types/providers/macos.d.ts.map +1 -1
  33. package/lib/types/skill.d.ts +7 -2
  34. package/lib/types/skill.d.ts.map +1 -1
  35. package/lib/types/tools.d.ts.map +1 -1
  36. package/lib/types/types.d.ts +3 -1
  37. package/lib/types/types.d.ts.map +1 -1
  38. package/lib/types/web.d.ts +2 -1
  39. package/lib/types/web.d.ts.map +1 -1
  40. package/lib/types.js.map +1 -1
  41. package/lib/web.js +34 -9
  42. package/lib/web.js.map +1 -1
  43. package/native/macos/Sources/Helper/CursorOverlay.swift +3 -0
  44. package/native/macos/Sources/Helper/main.swift +35 -19
  45. package/native/macos/bin/dsh-computer-use-helper +0 -0
  46. package/native/macos/manifest.json +3 -3
  47. package/package.json +100 -41
  48. package/scripts/build-client.mjs +0 -0
  49. package/scripts/build-native.mjs +0 -0
  50. package/scripts/model-e2e.mjs +7 -3
  51. package/scripts/session-transcript.mjs +4 -3
  52. package/scripts/validate.mjs +0 -0
  53. package/src/client/index.tsx +4 -2
  54. package/src/config.ts +53 -21
  55. package/src/confirmations.ts +1 -1
  56. package/src/exposure.ts +20 -6
  57. package/src/index.ts +5 -1
  58. package/src/leases.ts +6 -2
  59. package/src/providers/macos.ts +31 -11
  60. package/src/skill.ts +14 -2
  61. package/src/tools.ts +3 -2
  62. package/src/types.ts +2 -1
  63. package/src/web.ts +37 -7
package/lib/web.js CHANGED
@@ -1,6 +1,6 @@
1
1
  /** Optional Web Settings and provider-health route. */
2
2
  import { SettingsConflictError } from '@deepseek-ai/dsh-settings';
3
- import { COMPUTER_USE_SETTINGS_NAMESPACE, } from "./config.js";
3
+ import { COMPUTER_USE_SETTINGS_NAMESPACE, resolveConfig, } from "./config.js";
4
4
  /** Exact same-origin Settings endpoint. */
5
5
  export const COMPUTER_USE_SETTINGS_ROUTE = '/_dsh/computer-use/settings';
6
6
  function isRecord(value) {
@@ -25,13 +25,19 @@ function responseJson(res, status, body) {
25
25
  function requestError(res, status, code, message) {
26
26
  responseJson(res, status, { ok: false, error: { code, message } });
27
27
  }
28
- function sameOriginPost(req) {
28
+ function sameOriginPost(req, authorizeUnmarkedPost) {
29
29
  const fetchSite = req.headers['sec-fetch-site'];
30
30
  if (fetchSite === 'cross-site')
31
31
  return false;
32
32
  const origin = req.headers.origin;
33
- if (origin === undefined)
34
- return fetchSite === 'same-origin' || fetchSite === 'same-site' || fetchSite === 'none';
33
+ if (origin === undefined) {
34
+ if (fetchSite === 'same-origin' || fetchSite === 'same-site' || fetchSite === 'none')
35
+ return true;
36
+ // Desktop strips browser origin metadata before forwarding, but injects a
37
+ // Host-issued signed cookie. Verify it through Connection's own fence;
38
+ // mere Cookie header presence is not authentication.
39
+ return fetchSite === undefined && authorizeUnmarkedPost?.(req) === true;
40
+ }
35
41
  const host = req.headers.host;
36
42
  if (host === undefined)
37
43
  return false;
@@ -78,11 +84,28 @@ function parseRequest(value) {
78
84
  function publicMessage(error) {
79
85
  return error instanceof Error ? error.message.slice(0, 1000) : String(error).slice(0, 1000);
80
86
  }
87
+ /**
88
+ * Whether one thrown value is the Settings provider's optimistic-concurrency
89
+ * conflict. The provider — not this module — throws it, so a second copy of
90
+ * `SettingsConflictError` (plugin-local versus host-local install) makes
91
+ * `instanceof` alone unreliable. Both the 0.1.5 and the older class carry the
92
+ * stable machine code `"SETTINGS_CONFLICT"`, which is checked first-class;
93
+ * `instanceof` stays as an equivalent additional branch.
94
+ */
95
+ function isSettingsConflict(error) {
96
+ if (error instanceof SettingsConflictError)
97
+ return true;
98
+ return typeof error === 'object'
99
+ && error !== null
100
+ && error.code === 'SETTINGS_CONFLICT';
101
+ }
81
102
  /** Same-origin backend used by the optional client Settings section. */
82
103
  export class ComputerUseWebBackend {
83
104
  ctx;
84
- constructor(ctx) {
105
+ authorizeUnmarkedPost;
106
+ constructor(ctx, authorizeUnmarkedPost) {
85
107
  this.ctx = ctx;
108
+ this.authorizeUnmarkedPost = authorizeUnmarkedPost;
86
109
  }
87
110
  /** Current browser-safe Settings and health state. */
88
111
  snapshot() {
@@ -116,7 +139,7 @@ export class ComputerUseWebBackend {
116
139
  requestError(res, 405, 'method-not-allowed', 'Use GET or POST');
117
140
  return;
118
141
  }
119
- if (!sameOriginPost(req)) {
142
+ if (!sameOriginPost(req, this.authorizeUnmarkedPost)) {
120
143
  requestError(res, 403, 'origin-rejected', 'The request must originate from this DSH Web application');
121
144
  return;
122
145
  }
@@ -132,6 +155,7 @@ export class ComputerUseWebBackend {
132
155
  if (parsed.action === 'save') {
133
156
  if (!this.ctx.settings.writable)
134
157
  throw new Error('settings provider is read-only');
158
+ resolveConfig(parsed.value);
135
159
  await this.ctx.settings.replace(COMPUTER_USE_SETTINGS_NAMESPACE, parsed.value, parsed.expectedRevision);
136
160
  }
137
161
  else if (parsed.action === 'health') {
@@ -143,7 +167,7 @@ export class ComputerUseWebBackend {
143
167
  responseJson(res, 200, { ok: true, value: this.snapshot() });
144
168
  }
145
169
  catch (error) {
146
- const conflict = error instanceof SettingsConflictError;
170
+ const conflict = isSettingsConflict(error);
147
171
  this.ctx.logger.warn('dsh-computer-use Web action=%s failed: %s', parsed.action, publicMessage(error));
148
172
  requestError(res, conflict ? 409 : 400, conflict ? 'settings-conflict' : 'action-failed', publicMessage(error));
149
173
  }
@@ -151,8 +175,9 @@ export class ComputerUseWebBackend {
151
175
  }
152
176
  /** Attach the optional route when a Web host is present. */
153
177
  export function installComputerUseWeb(ctx) {
154
- const backend = new ComputerUseWebBackend(ctx);
155
- ctx.inject(['webServer'], (webCtx) => {
178
+ ctx.inject(['webServer', 'connection'], (webCtx) => {
179
+ const connection = webCtx.connection;
180
+ const backend = new ComputerUseWebBackend(webCtx, (req) => typeof connection?.requestRejection === 'function' && connection.requestRejection(req) === undefined);
156
181
  webCtx.effect(() => webCtx.webServer.register({
157
182
  kind: 'exact',
158
183
  path: COMPUTER_USE_SETTINGS_ROUTE,
package/lib/web.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"web.js","sourceRoot":"","sources":["../src/web.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"web.js","sourceRoot":"","sources":["../src/web.ts"],"names":[],"mappings":"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"}
@@ -204,6 +204,9 @@ private final class CursorOverlayController: NSObject {
204
204
  window.isOpaque = false
205
205
  window.backgroundColor = .clear
206
206
  window.hasShadow = false
207
+ // Target loss must remove the indicator immediately, without AppKit's
208
+ // panel fade leaving it visible over an unrelated foreground app.
209
+ window.animationBehavior = .none
207
210
  window.ignoresMouseEvents = true
208
211
  window.hidesOnDeactivate = false
209
212
  window.becomesKeyOnlyIfNeeded = true
@@ -6,7 +6,7 @@ import Darwin
6
6
  import Foundation
7
7
  import ScreenCaptureKit
8
8
 
9
- private let helperVersion = "0.3.1"
9
+ private let helperVersion = "0.3.3"
10
10
 
11
11
  private struct HelperError: Error {
12
12
  let code: String
@@ -501,22 +501,27 @@ private func captureWindow(_ snapshot: ObservationSnapshot, path: String, requir
501
501
  return nil
502
502
  }
503
503
  let expectedId = (snapshot.windowJSON?["id"] as? NSNumber)?.uint32Value
504
- let expectedTitle = snapshot.windowJSON?["title"] as? String
504
+ let expectedFrame = (snapshot.windowJSON?["frame"] as? [String: Any]).flatMap { try? cgRect($0) }
505
505
  let windows = content.windows.filter { $0.owningApplication?.processID == snapshot.app.processIdentifier }
506
506
  let selected = windows.first { window in
507
- if let expectedId, window.windowID == expectedId { return true }
508
- if let expectedTitle, !expectedTitle.isEmpty, window.title == expectedTitle { return true }
509
- return false
510
- } ?? windows.max { $0.frame.width * $0.frame.height < $1.frame.width * $1.frame.height }
507
+ if let expectedId { return window.windowID == expectedId }
508
+ guard let expectedFrame else { return false }
509
+ let tolerance: CGFloat = 2
510
+ return abs(window.frame.minX - expectedFrame.minX) <= tolerance
511
+ && abs(window.frame.minY - expectedFrame.minY) <= tolerance
512
+ && abs(window.frame.width - expectedFrame.width) <= tolerance
513
+ && abs(window.frame.height - expectedFrame.height) <= tolerance
514
+ }
511
515
  guard let selected else {
512
- if required { throw fail("COMPUTER_TARGET_UNAVAILABLE", "no capturable window belongs to the selected application") }
516
+ if required { throw fail("COMPUTER_TARGET_UNAVAILABLE", "the observed window is not available for capture") }
513
517
  return nil
514
518
  }
519
+ let filter = SCContentFilter(desktopIndependentWindow: selected)
515
520
  let configuration = SCStreamConfiguration()
516
- configuration.width = max(1, Int(selected.frame.width.rounded()))
517
- configuration.height = max(1, Int(selected.frame.height.rounded()))
521
+ let pixelScale = CGFloat(filter.pointPixelScale)
522
+ configuration.width = max(1, Int((selected.frame.width * pixelScale).rounded()))
523
+ configuration.height = max(1, Int((selected.frame.height * pixelScale).rounded()))
518
524
  configuration.showsCursor = false
519
- let filter = SCContentFilter(desktopIndependentWindow: selected)
520
525
  let image: CGImage
521
526
  do {
522
527
  image = try await SCScreenshotManager.captureImage(contentFilter: filter, configuration: configuration)
@@ -583,25 +588,36 @@ private func cgRect(_ frame: [String: Any]) throws -> CGRect {
583
588
 
584
589
  private func windowAtPoint(app: NSRunningApplication, point: CGPoint) throws -> (windowNumber: Int64, frame: CGRect) {
585
590
  guard let windows = CGWindowListCopyWindowInfo(
586
- [.optionOnScreenOnly, .excludeDesktopElements],
591
+ [.optionAll, .excludeDesktopElements],
587
592
  kCGNullWindowID
588
593
  ) as? [[String: Any]] else {
589
594
  throw fail("COMPUTER_TARGET_UNAVAILABLE", "CoreGraphics window list is unavailable")
590
595
  }
591
- // CGWindowListCopyWindowInfo orders on-screen windows front to back, so the
592
- // first match is the topmost window of the selected app at the point.
593
- guard let window = windows.first(where: { candidate in
596
+ let appWindows = windows.filter { candidate in
594
597
  guard (candidate[kCGWindowOwnerPID as String] as? NSNumber)?.int32Value == app.processIdentifier,
595
- (candidate[kCGWindowLayer as String] as? NSNumber)?.intValue == 0,
596
- let bounds = candidate[kCGWindowBounds as String] as? [String: Any],
597
- let frame = CGRect(dictionaryRepresentation: bounds as CFDictionary),
598
- frame.contains(point) else { return false }
598
+ (candidate[kCGWindowLayer as String] as? NSNumber)?.intValue == 0 else { return false }
599
599
  return true
600
+ }
601
+ let containing = appWindows.filter { candidate in
602
+ guard let bounds = candidate[kCGWindowBounds as String] as? [String: Any],
603
+ let frame = CGRect(dictionaryRepresentation: bounds as CFDictionary) else { return false }
604
+ return frame.contains(point)
605
+ }
606
+ // optionAll can recover windows omitted by optionOnScreenOnly on some
607
+ // multi-display layouts. Never route pointer input to a hidden window.
608
+ guard let window = containing.first(where: {
609
+ ($0[kCGWindowIsOnscreen as String] as? NSNumber)?.boolValue == true
600
610
  }),
601
611
  let windowNumber = (window[kCGWindowNumber as String] as? NSNumber)?.int64Value,
602
612
  let bounds = window[kCGWindowBounds as String] as? [String: Any],
603
613
  let frame = CGRect(dictionaryRepresentation: bounds as CFDictionary) else {
604
- throw fail("COMPUTER_TARGET_UNAVAILABLE", "no on-screen window of the selected app contains the requested coordinate")
614
+ if !containing.isEmpty {
615
+ throw fail("COMPUTER_TARGET_UNAVAILABLE", "the selected app window contains the coordinate but CoreGraphics does not mark it on-screen")
616
+ }
617
+ if appWindows.isEmpty {
618
+ throw fail("COMPUTER_TARGET_UNAVAILABLE", "the selected app has no enumerable layer-zero window in CoreGraphics")
619
+ }
620
+ throw fail("COMPUTER_TARGET_UNAVAILABLE", "coordinate is outside the selected app's enumerable windows")
605
621
  }
606
622
  return (windowNumber, frame)
607
623
  }
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
- "helperVersion": "0.3.1",
4
- "sourceSha256": "2239f57042d97cb55c7cf1ad2235ea3493457f761dbe16533d2bc9e302cbe42d",
3
+ "helperVersion": "0.3.3",
4
+ "sourceSha256": "b5de65a33e53689c5b490d8f6641a6577ba61a6f6f45798210b0df65bd0ced07",
5
5
  "binary": {
6
6
  "path": "bin/dsh-computer-use-helper",
7
- "sha256": "545db079a227affc503c93ffed69cadf9306407de8f8bc8494d90f2948a3f600",
7
+ "sha256": "55325e0008d755bf2b1d408bcfd5cdb80842ec2a0fa5312da6961eb65d7d6dd3",
8
8
  "architectures": [
9
9
  "arm64",
10
10
  "x86_64"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@anionex/dsh-computer-use",
3
- "version": "0.3.1",
3
+ "version": "0.3.3",
4
4
  "description": "Accessibility-first macOS Computer Use capability for DeepSeek Harness with stale-observation protection, app leases, confirmations, screenshots, and Web diagnostics",
5
5
  "keywords": [
6
6
  "deepseek",
@@ -18,6 +18,7 @@
18
18
  "url": "https://github.com/Anionex/dsh-computer-use/issues"
19
19
  },
20
20
  "type": "module",
21
+ "packageManager": "pnpm@11.20.0",
21
22
  "engines": {
22
23
  "node": "^22.19.0 || >=24.0.0"
23
24
  },
@@ -55,44 +56,79 @@
55
56
  "cordis.patch.yml",
56
57
  "README.md",
57
58
  "README.zh.md",
58
- "LICENSE"
59
+ "LICENSE",
60
+ "CHANGELOG.md"
59
61
  ],
62
+ "scripts": {
63
+ "native:build": "node scripts/build-native.mjs",
64
+ "check:native": "node scripts/check-native.mjs",
65
+ "build": "pnpm run native:build && tsc -p tsconfig.json && tsc -p tsconfig.client.json && node scripts/build-client.mjs",
66
+ "prepack": "pnpm run build",
67
+ "test": "pnpm run build && vitest run tests",
68
+ "test:model": "node scripts/model-e2e.mjs",
69
+ "test:watch": "vitest tests",
70
+ "validate": "node scripts/validate.mjs --lane all",
71
+ "validate:local": "node scripts/validate.mjs --lane local",
72
+ "validate:profile": "node scripts/validate.mjs --lane profile",
73
+ "validate:model": "node scripts/model-e2e.mjs",
74
+ "validate:release": "pnpm run validate && pnpm run validate:model"
75
+ },
60
76
  "dsh": {
61
77
  "bundle": {
62
78
  "patch": "./cordis.patch.yml"
63
79
  },
64
80
  "client": {
65
81
  "inject": [
66
- "@deepseek-ai/dsh-client-runtime",
67
- "@deepseek-ai/dsh-client-ui-settings",
82
+ "@deepseek-ai/dsh-client-ui-renderer",
83
+ "@deepseek-ai/dsh-client-ui-settings-general",
68
84
  "@deepseek-ai/dsh-client-locale",
69
- "@deepseek-ai/dsh-api-remotes"
85
+ "@deepseek-ai/dsh-api-remotes",
86
+ "@deepseek-ai/dsh-client-connection"
70
87
  ],
71
88
  "platform": "web"
89
+ },
90
+ "compatibility": {
91
+ "dsh": ">=0.1.0-rc.8 <0.2.0 || >=0.1.1-rc.1 <0.2.0 || >=0.1.2-alpha.3 <0.2.0 || >=0.1.5-rc.1 <0.2.0 || 0.1.7-rc.2 || 0.2.0-rc.2",
92
+ "dshReleases": {
93
+ "0.1.0-rc.8": "compatible",
94
+ "0.1.1-rc.1": "compatible",
95
+ "0.1.1-rc.2": "compatible",
96
+ "0.1.2-alpha.3": "compatible",
97
+ "0.1.2-alpha.4": "compatible",
98
+ "0.1.2-alpha.5": "compatible",
99
+ "0.1.5-alpha.1": "compatible",
100
+ "0.1.5-alpha.2": "compatible",
101
+ "0.1.5-rc.1": "compatible",
102
+ "0.1.7-rc.2": "compatible",
103
+ "0.2.0-rc.2": "compatible"
104
+ },
105
+ "profiles": [
106
+ "web"
107
+ ]
72
108
  }
73
109
  },
74
110
  "peerDependencies": {
75
- "@deepseek-ai/dsh-agent": "^0.1.0-rc.6",
76
- "@deepseek-ai/dsh-api-remotes": "^0.1.0-rc.6",
77
- "@deepseek-ai/dsh-brand": "^0.1.0-rc.6",
78
- "@deepseek-ai/dsh-client-locale": "^0.1.0-rc.6",
79
- "@deepseek-ai/dsh-client-runtime": "^0.1.0-rc.6",
80
- "@deepseek-ai/dsh-client-ui-primitives": "^0.1.0-rc.6",
81
- "@deepseek-ai/dsh-client-ui-settings": "^0.1.0-rc.6",
82
- "@deepseek-ai/dsh-client-ui-slots": "^0.1.0-rc.6",
83
- "@deepseek-ai/dsh-host-webserver": "^0.1.0-rc.6",
84
- "@deepseek-ai/dsh-llm": "^0.1.0-rc.6",
85
- "@deepseek-ai/dsh-session": "^0.1.0-rc.6",
86
- "@deepseek-ai/dsh-settings": "^0.1.0-rc.6",
87
- "@deepseek-ai/dsh-skill": "^0.1.0-rc.6",
88
- "@deepseek-ai/dsh-storage-domain": "^0.1.0-rc.6",
89
- "@deepseek-ai/dsh-subprocess": "^0.1.0-rc.6",
90
- "@deepseek-ai/dsh-system-prompt": "^0.1.0-rc.6",
91
- "@deepseek-ai/dsh-tools": "^0.1.0-rc.6",
92
- "@deepseek-ai/dsh-user-approval": "^0.1.0-rc.6",
111
+ "@deepseek-ai/dsh-agent": "^0.1.0-rc.6 || 0.1.7-rc.2 || 0.2.0-rc.2",
112
+ "@deepseek-ai/dsh-api-remotes": "^0.1.0-rc.6 || 0.1.7-rc.2 || 0.2.0-rc.2",
113
+ "@deepseek-ai/dsh-brand": "^0.1.0-rc.6 || 0.1.7-rc.2 || 0.2.0-rc.2",
114
+ "@deepseek-ai/dsh-client-locale": "^0.1.0-rc.6 || 0.1.7-rc.2 || 0.2.0-rc.2",
115
+ "@deepseek-ai/dsh-client-ui-primitives": "^0.1.0-rc.6 || 0.1.7-rc.2 || 0.2.0-rc.2",
116
+ "@deepseek-ai/dsh-client-ui-settings": "^0.1.0-rc.6 || 0.1.7-rc.2 || 0.2.0-rc.2",
117
+ "@deepseek-ai/dsh-client-ui-slots": "^0.1.0-rc.6 || 0.1.7-rc.2 || 0.2.0-rc.2",
118
+ "@deepseek-ai/dsh-host-webserver": "^0.1.0-rc.6 || 0.1.7-rc.2 || 0.2.0-rc.2",
119
+ "@deepseek-ai/dsh-llm": "^0.1.0-rc.6 || 0.1.7-rc.2 || 0.2.0-rc.2",
120
+ "@deepseek-ai/dsh-session": "^0.1.0-rc.6 || 0.1.7-rc.2 || 0.2.0-rc.2",
121
+ "@deepseek-ai/dsh-settings": "^0.1.0-rc.6 || 0.1.7-rc.2 || 0.2.0-rc.2",
122
+ "@deepseek-ai/dsh-skill": "^0.1.0-rc.6 || 0.1.7-rc.2 || 0.2.0-rc.2",
123
+ "@deepseek-ai/dsh-storage-domain": "^0.1.0-rc.6 || 0.1.7-rc.2 || 0.2.0-rc.2",
124
+ "@deepseek-ai/dsh-subprocess": "^0.1.0-rc.6 || 0.1.7-rc.2 || 0.2.0-rc.2",
125
+ "@deepseek-ai/dsh-system-prompt": "^0.1.0-rc.6 || 0.1.7-rc.2 || 0.2.0-rc.2",
126
+ "@deepseek-ai/dsh-tools": "^0.1.0-rc.6 || 0.1.7-rc.2 || 0.2.0-rc.2",
127
+ "@deepseek-ai/dsh-user-approval": "^0.1.0-rc.6 || 0.1.7-rc.2 || 0.2.0-rc.2",
93
128
  "@deepseek-ai/cordis": "^4.0.1",
94
129
  "react": "^18.2.0",
95
- "@deepseek-ai/schemastery": "^3.18.1"
130
+ "@deepseek-ai/schemastery": "^3.18.1",
131
+ "@deepseek-ai/dsh-util-values": "^0.1.0-rc.6 || 0.2.0-rc.2"
96
132
  },
97
133
  "peerDependenciesMeta": {
98
134
  "@deepseek-ai/dsh-host-webserver": {
@@ -104,23 +140,46 @@
104
140
  "@types/react": "~18.3.1",
105
141
  "@types/react-dom": "~18.3.0",
106
142
  "typescript": "^6.0.3",
107
- "vitest": "^4.1.10"
143
+ "vitest": "^4.1.10",
144
+ "@deepseek-ai/dsh-agent": "0.2.0-rc.2",
145
+ "@deepseek-ai/dsh-api-remotes": "0.2.0-rc.2",
146
+ "@deepseek-ai/dsh-brand": "0.2.0-rc.2",
147
+ "@deepseek-ai/dsh-client-locale": "0.2.0-rc.2",
148
+ "@deepseek-ai/dsh-client-ui-primitives": "0.2.0-rc.2",
149
+ "@deepseek-ai/dsh-client-ui-settings": "0.2.0-rc.2",
150
+ "@deepseek-ai/dsh-client-ui-slots": "0.2.0-rc.2",
151
+ "@deepseek-ai/dsh-host-webserver": "0.2.0-rc.2",
152
+ "@deepseek-ai/dsh-llm": "0.2.0-rc.2",
153
+ "@deepseek-ai/dsh-session": "0.2.0-rc.2",
154
+ "@deepseek-ai/dsh-settings": "0.2.0-rc.2",
155
+ "@deepseek-ai/dsh-skill": "0.2.0-rc.2",
156
+ "@deepseek-ai/dsh-storage-domain": "0.2.0-rc.2",
157
+ "@deepseek-ai/dsh-subprocess": "0.2.0-rc.2",
158
+ "@deepseek-ai/dsh-system-prompt": "0.2.0-rc.2",
159
+ "@deepseek-ai/dsh-tools": "0.2.0-rc.2",
160
+ "@deepseek-ai/dsh-user-approval": "0.2.0-rc.2",
161
+ "@deepseek-ai/cordis": "4.0.4",
162
+ "@deepseek-ai/schemastery": "3.18.4",
163
+ "@deepseek-ai/dsh-util-values": "0.2.0-rc.2",
164
+ "@deepseek-ai/dsh-client-ui-renderer": "0.2.0-rc.2",
165
+ "@deepseek-ai/dsh-client-connection": "0.2.0-rc.2",
166
+ "@deepseek-ai/dsh-scope": "0.2.0-rc.2",
167
+ "@deepseek-ai/dsh-storage": "0.2.0-rc.2",
168
+ "@deepseek-ai/cosmokit": "1.8.5",
169
+ "@deepseek-ai/dsh-credentials": "0.2.0-rc.2",
170
+ "@deepseek-ai/dsh-typert-protocol": "0.2.0-rc.2",
171
+ "@deepseek-ai/dsh-util-crypto": "0.2.0-rc.2",
172
+ "@deepseek-ai/dsh-timeout": "0.2.0-rc.2",
173
+ "@deepseek-ai/dsh-sandbox": "0.2.0-rc.2",
174
+ "@deepseek-ai/dsh-http-proxy": "0.2.0-rc.2",
175
+ "@deepseek-ai/cordis-plugin-loader": "1.0.5",
176
+ "@deepseek-ai/dsh-util-workspace-path": "0.2.0-rc.2",
177
+ "@deepseek-ai/dsh-client-store": "0.2.0-rc.2",
178
+ "@deepseek-ai/dsh-util-code-language": "0.2.0-rc.2",
179
+ "@deepseek-ai/dsh-deque": "0.2.0-rc.2"
108
180
  },
109
181
  "dependencies": {
110
- "zod": "^4.4.3"
182
+ "zod": "4.4.3"
111
183
  },
112
- "license": "MIT",
113
- "scripts": {
114
- "native:build": "node scripts/build-native.mjs",
115
- "check:native": "node scripts/check-native.mjs",
116
- "build": "pnpm run native:build && tsc -p tsconfig.json && tsc -p tsconfig.client.json && node scripts/build-client.mjs",
117
- "test": "pnpm run build && vitest run tests",
118
- "test:model": "node scripts/model-e2e.mjs",
119
- "test:watch": "vitest tests",
120
- "validate": "node scripts/validate.mjs --lane all",
121
- "validate:local": "node scripts/validate.mjs --lane local",
122
- "validate:profile": "node scripts/validate.mjs --lane profile",
123
- "validate:model": "node scripts/model-e2e.mjs",
124
- "validate:release": "pnpm run validate && pnpm run validate:model"
125
- }
126
- }
184
+ "license": "MIT"
185
+ }
File without changes
File without changes
@@ -45,8 +45,8 @@ function externalCommand(command, args) {
45
45
  // A long-running Node harness can leave DSH without its internal ESM loader.
46
46
  // A login shell matches the real CLI entry and keeps profile package resolution isolated.
47
47
  return command === 'dsh' && process.platform === 'darwin'
48
- ? { command: '/bin/bash', args: ['-lc', 'exec dsh "$@"', 'dsh', ...args] }
49
- : { command, args }
48
+ ? { command: '/bin/bash', args: ['-lc', 'exec "$@"', 'dsh', process.env.DSH_TEST_CLI ?? 'dsh', ...args] }
49
+ : { command: command === 'dsh' ? process.env.DSH_TEST_CLI ?? command : command, args }
50
50
  }
51
51
 
52
52
  async function runCommand(name, command, args, options = {}) {
@@ -180,6 +180,8 @@ async function realModelWorkflow() {
180
180
  }
181
181
 
182
182
  const tarball = await findTarball(packing)
183
+ await mkdir(join(home, 'profiles', 'headless'), { recursive: true })
184
+ await writeFile(join(home, 'profiles', 'headless', 'pnpm-workspace.yaml'), 'nodeLinker: hoisted\nautoInstallPeers: false\n')
183
185
  await runCommand(
184
186
  'install dsh-computer-use tarball',
185
187
  'dsh',
@@ -215,6 +217,8 @@ async function realModelWorkflow() {
215
217
  ].join('\n'))
216
218
 
217
219
  const prompt = `/computer-use\n\nUse the dsh-computer-use capability to operate the running deterministic macOS fixture whose bundle id is ${BUNDLE_ID}. Load the Skill, list running applications, select the exact fixture process, observe it with a required screenshot and full Accessibility state, locate the element labelled "Targeted pointer probe", and click that current element using its observation id and element index with allowCoordinateFallback=true. Confirm from the fresh post-action observation that the status reads "Status: pointer click". Use only the focused computer-use Tools for UI observation and input; do not use shell, AppleScript, JXA, direct file edits, or coordinate guessing. Finish immediately after confirming the pointer-click state.`
220
+ const hostVersion = await runCommand('target DSH version', 'dsh', ['--version'])
221
+ const defaultBaseURL = hostVersion.stdout.includes('0.2.') ? 'https://api.deepseek.com/anthropic' : 'https://api.deepseek.com/v1'
218
222
  const result = await runCommand(
219
223
  'DeepSeek V4 computer-use fixture workflow',
220
224
  'dsh',
@@ -226,7 +230,7 @@ async function realModelWorkflow() {
226
230
  DSH_TELEMETRY_DISABLED: '1',
227
231
  DSH_PERMISSION_MODE: 'workspace-write',
228
232
  DEEPSEEK_API_KEY: requiredEnvironment('DEEPSEEK_API_KEY'),
229
- DEEPSEEK_BASE_URL: process.env.DEEPSEEK_BASE_URL?.trim() || 'https://api.deepseek.com/v1',
233
+ DEEPSEEK_BASE_URL: process.env.DEEPSEEK_BASE_URL?.trim() || defaultBaseURL,
230
234
  },
231
235
  timeoutMs: 600_000,
232
236
  },
@@ -53,7 +53,7 @@ async function sessionLogs(directory) {
53
53
  for (const entry of await readdir(directory, { withFileTypes: true }).catch(() => [])) {
54
54
  const path = join(directory, entry.name)
55
55
  if (entry.isDirectory()) result.push(...await sessionLogs(path))
56
- else if (entry.isFile() && (entry.name === 'session.jsonl' || entry.name === 'session.jsonl.zstd')) result.push(path)
56
+ else if (entry.isFile() && /^session(?:\.v\d+)?\.jsonl(?:\.zstd)?$/u.test(entry.name)) result.push(path)
57
57
  }
58
58
  return result
59
59
  }
@@ -71,8 +71,9 @@ function resultValue(event) {
71
71
  const outer = event?.data?.message?.content
72
72
  if (!Array.isArray(outer)) return undefined
73
73
  for (const block of outer) {
74
- if (block?.type !== 'tool-result' || !Array.isArray(block.content)) continue
75
- for (const content of block.content) {
74
+ // Session v4 stores tool-role text directly; earlier logs wrap it.
75
+ const contents = block?.type === 'tool-result' && Array.isArray(block.content) ? block.content : [block]
76
+ for (const content of contents) {
76
77
  if (content?.type !== 'text' || typeof content.text !== 'string') continue
77
78
  try { return JSON.parse(content.text) }
78
79
  catch { return undefined }
File without changes
@@ -2,7 +2,9 @@
2
2
 
3
3
  import { useEffect, useState, useSyncExternalStore, type ReactNode } from 'react'
4
4
  import { Button, Input } from '@deepseek-ai/dsh-client-ui-primitives'
5
- import type { ClientContext } from '@deepseek-ai/dsh-client-runtime/client'
5
+ import type { Context as ClientContext } from '@deepseek-ai/cordis'
6
+ import type {} from '@deepseek-ai/dsh-client-ui-renderer/client'
7
+ import type {} from '@deepseek-ai/dsh-client-connection/client'
6
8
  import type {} from '@deepseek-ai/dsh-api-remotes/client'
7
9
  import type {} from '@deepseek-ai/dsh-client-ui-settings/client'
8
10
  import type {} from '@deepseek-ai/dsh-client-locale/client'
@@ -561,7 +563,7 @@ export function apply(ctx: ClientContext): void {
561
563
  const controller = new ComputerUseSettingsController()
562
564
  ctx.effect(() => {
563
565
  const disposers = [
564
- ctx.remote.$on('settings/document-updated', ns => { if (ns === NS) controller.refreshIfLoaded() }),
566
+ ctx.remote.$on('settings/document-updated', (ns: string) => { if (ns === NS) controller.refreshIfLoaded() }),
565
567
  ctx.on('connection/reset', () => { controller.refreshIfLoaded() }),
566
568
  ]
567
569
  return () => { for (const dispose of disposers) dispose() }
package/src/config.ts CHANGED
@@ -2,11 +2,22 @@
2
2
 
3
3
  import z from '@deepseek-ai/schemastery'
4
4
  import type Schema from '@deepseek-ai/schemastery'
5
- import { settingsNamespace } from '@deepseek-ai/dsh-settings'
5
+ import * as SettingsModule from '@deepseek-ai/dsh-settings'
6
+ import type { SettingsNamespace } from '@deepseek-ai/dsh-settings'
6
7
  import { ComputerUseError } from './errors.ts'
7
8
 
8
- /** Settings document namespace owned by this package. */
9
- export const COMPUTER_USE_SETTINGS_NAMESPACE = settingsNamespace('computer-use')
9
+ /**
10
+ * Settings document namespace owned by this package.
11
+ *
12
+ * DSH 0.1.5 removed the `settingsNamespace()` runtime brander: the namespace is
13
+ * a compile-time-only brand and the official convention is a plain string
14
+ * literal (0.1.5's own `ui-chat` client does `const CHAT_SETTINGS_NAMESPACE =
15
+ * "ui-chat"`). The literal below satisfies the 0.1.5 grammar
16
+ * `/^[a-z][a-z0-9-]*$/` and, because `SettingsNamespace` is a type-only export
17
+ * in every supported release, the cast is erased at build time — the value that
18
+ * reaches the host is exactly `'computer-use'`.
19
+ */
20
+ export const COMPUTER_USE_SETTINGS_NAMESPACE: SettingsNamespace = 'computer-use' as SettingsNamespace
10
21
 
11
22
  /** One persisted application grant. Wildcards are intentionally unsupported. */
12
23
  export interface ComputerUseAppGrant {
@@ -50,23 +61,44 @@ export interface ComputerUseConfig {
50
61
  grants?: ComputerUseAppGrant[]
51
62
  }
52
63
 
64
+ /** DSH 0.1.7 derives editable Settings fields from volatile Config fields.
65
+ * Legacy Settings can coexist with a newer Schemastery installation. Only
66
+ * enable references when the host Settings service uses Config-derived forms.
67
+ */
68
+ function live<T>(field: Schema<T>): Schema<T> {
69
+ const candidate = field as Schema<T> & { volatile?: () => Schema<T> }
70
+ return 'SettingsForms' in SettingsModule && typeof candidate.volatile === 'function'
71
+ ? candidate.volatile() as unknown as Schema<T>
72
+ : field
73
+ }
74
+
75
+ /** Read the newer Loader's stable references without changing the legacy shape. */
76
+ export function readComputerUseConfig(config: ComputerUseConfig): ComputerUseConfig {
77
+ return Object.fromEntries(Object.entries(config).map(([key, value]) => [
78
+ key,
79
+ value !== null && typeof value === 'object' && 'get' in value && typeof value.get === 'function'
80
+ ? value.get()
81
+ : value,
82
+ ])) as ComputerUseConfig
83
+ }
84
+
53
85
  /** Configuration schema used by Cordis and the Settings provider. */
54
86
  export const Config: Schema<ComputerUseConfig> = z.object({
55
- observationTtlMs: z.number().default(0),
56
- confirmationTtlMs: z.number().default(300000),
57
- actionTimeoutMs: z.number().default(15000),
58
- settleMs: z.number().default(250),
59
- maxSettleMs: z.number().default(5000),
60
- maxNodes: z.number().default(500),
61
- maxDepth: z.number().default(14),
62
- maxTextBytes: z.number().default(64000),
63
- maxScreenshotBytes: z.number().default(33554432),
64
- artifactRoot: z.string().default('.dsh-computer-use/artifacts'),
65
- helper: z.object({
87
+ observationTtlMs: live(z.number().default(0)),
88
+ confirmationTtlMs: live(z.number().default(300000)),
89
+ actionTimeoutMs: live(z.number().default(15000)),
90
+ settleMs: live(z.number().default(250)),
91
+ maxSettleMs: live(z.number().default(5000)),
92
+ maxNodes: live(z.number().default(500)),
93
+ maxDepth: live(z.number().default(14)),
94
+ maxTextBytes: live(z.number().default(64000)),
95
+ maxScreenshotBytes: live(z.number().default(33554432)),
96
+ artifactRoot: live(z.string().default('.dsh-computer-use/artifacts')),
97
+ helper: live(z.object({
66
98
  path: z.string(),
67
99
  allowSourceBuild: z.boolean().default(false),
68
- }),
69
- interaction: z.object({
100
+ })),
101
+ interaction: live(z.object({
70
102
  focusPolicy: z.union(['preserve', 'activate']).default('preserve'),
71
103
  keyboardPolicy: z.union(['preserve', 'activate']).default('preserve'),
72
104
  pointerInputPolicy: z.union(['deny', 'targeted']).default('targeted'),
@@ -76,14 +108,14 @@ export const Config: Schema<ComputerUseConfig> = z.object({
76
108
  cursorAccelerationPxPerSecondSquared: z.number().default(6000),
77
109
  cursorClickDelayMs: z.number().default(90),
78
110
  cursorAutoHideMs: z.number().default(0),
79
- }),
80
- allowAllApps: z.boolean().default(false),
81
- grants: z.array(z.object({
111
+ })),
112
+ allowAllApps: live(z.boolean().default(false)),
113
+ grants: live(z.array(z.object({
82
114
  bundleId: z.string(),
83
115
  read: z.boolean().default(false),
84
116
  control: z.boolean().default(false),
85
- })).default([]),
86
- })
117
+ })).default([])),
118
+ }) as unknown as Schema<ComputerUseConfig>
87
119
 
88
120
  /** Fully defaulted configuration consumed at runtime. */
89
121
  export interface ResolvedComputerUseConfig {