@ai-sdk/harness-opencode 1.0.119 → 1.0.120

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 CHANGED
@@ -1,5 +1,16 @@
1
1
  # @ai-sdk/harness-opencode
2
2
 
3
+ ## 1.0.120
4
+
5
+ ### Patch Changes
6
+
7
+ - d975097: feat(harness): add `AbortSignal` to `SandboxChannel.connect`, plus a `sleep()` helper function
8
+ - 01a671a: fix(harness-opencode): allow external directory access with `permissionMode: "allow-all"`
9
+ - 80b969d: feat(harness-opencode): allow consumers to configure bridge reconnect timing
10
+ - Updated dependencies [d975097]
11
+ - Updated dependencies [b2baeba]
12
+ - @ai-sdk/harness@1.0.118
13
+
3
14
  ## 1.0.119
4
15
 
5
16
  ### Patch Changes
@@ -646,6 +646,7 @@ function serialiseError(err) {
646
646
 
647
647
  // src/bridge/index.ts
648
648
  import { randomUUID as randomUUID2 } from "crypto";
649
+ import { realpathSync } from "fs";
649
650
  import path2 from "path";
650
651
  import { argv, env as procEnv2 } from "process";
651
652
  import { isDeepStrictEqual } from "util";
@@ -2787,9 +2788,6 @@ async function selectPermissionReply({
2787
2788
  emit
2788
2789
  }) {
2789
2790
  const toolName = toPermissionToolName(action);
2790
- if (resources.some((resource) => isExternalPath(resource))) {
2791
- return { reply: "reject", message: "External directory access rejected." };
2792
- }
2793
2791
  if (isBuiltinToolInactive({ toolName, toolFiltering: builtinToolFiltering })) {
2794
2792
  emit({
2795
2793
  type: "tool-approval-request",
@@ -2805,6 +2803,9 @@ async function selectPermissionReply({
2805
2803
  if (!permissionMode || permissionMode === "allow-all") {
2806
2804
  return { reply: "always" };
2807
2805
  }
2806
+ if (resources.some((resource) => isExternalPath(resource))) {
2807
+ return { reply: "reject", message: "External directory access rejected." };
2808
+ }
2808
2809
  const kind = TOOL_KIND[toolName] ?? "bash";
2809
2810
  const allowed = permissionMode === "allow-edits" ? kind === "readonly" || kind === "edit" : kind === "readonly";
2810
2811
  if (allowed) return { reply: "always" };
@@ -2847,12 +2848,26 @@ function isBuiltinToolInactive(input) {
2847
2848
  }
2848
2849
  function isExternalPath(resource) {
2849
2850
  if (!path2.isAbsolute(resource)) return false;
2850
- const normalized = path2.resolve(resource);
2851
- return !isPathInsideOrEqual(normalized, workdir) && (!skillsDir || !isPathInsideOrEqual(normalized, skillsDir));
2851
+ return !isPathInsideOrEqual(resource, workdir) && (!skillsDir || !isPathInsideOrEqual(resource, skillsDir));
2852
2852
  }
2853
2853
  function isPathInsideOrEqual(file, root) {
2854
- const normalizedRoot = path2.resolve(root);
2855
- return file === normalizedRoot || file.startsWith(`${normalizedRoot}/`);
2854
+ const relative = path2.relative(
2855
+ canonicalizeForContainment(root),
2856
+ canonicalizeForContainment(file)
2857
+ );
2858
+ return relative === "" || relative !== ".." && !relative.startsWith(`..${path2.sep}`) && !path2.isAbsolute(relative);
2859
+ }
2860
+ function canonicalizeForContainment(inputPath) {
2861
+ const normalized = path2.resolve(inputPath);
2862
+ try {
2863
+ return realpathSync.native(normalized);
2864
+ } catch {
2865
+ const parent = path2.dirname(normalized);
2866
+ return parent === normalized ? normalized : path2.join(
2867
+ canonicalizeForContainment(parent),
2868
+ path2.basename(normalized)
2869
+ );
2870
+ }
2856
2871
  }
2857
2872
  function toWireToolName(nativeName) {
2858
2873
  return NATIVE_TO_COMMON[nativeName] ?? OPENCODE_TO_WIRE[nativeName] ?? nativeName;