dsh-home-hosted 0.1.3 → 0.1.4

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/docs/DESIGN.md CHANGED
@@ -31,6 +31,16 @@ node_modules (flat or pnpm), then PATH — and forwards its argv, so the entry
31
31
  survives plugin upgrades and profile reinstalls. The plugin preflights the
32
32
  launcher the way the unit invokes it and shows the version it answers.
33
33
 
34
+ ## Approvals follow the session's sandbox
35
+
36
+ A mutating agent tool asks the approval service only when the calling session is
37
+ *not* already `danger-full-access` (`ctx.sandboxPolicy.resolve({ session })`).
38
+ Asking anyway made a Full-access run fail whenever the deployment's approvals
39
+ auto-reject — the session had already granted exactly what the tool was asking
40
+ about. Below full access the tool still asks and still fails closed, and the
41
+ refusal names the mode and the remedy (Full access, or an answerable approval
42
+ channel).
43
+
34
44
  ## Config compatibility
35
45
 
36
46
  `onPortConflict: kill` did not exist before home-hosted 0.6.0, and an older panel
package/lib/index.js CHANGED
@@ -3591,6 +3591,17 @@ var SettingsStore = class {
3591
3591
 
3592
3592
  // src/tools.ts
3593
3593
  import { defineTool } from "@deepseek-ai/dsh-tools";
3594
+ function sandboxMode(ctx, exec) {
3595
+ const policy = ctx.get("sandboxPolicy");
3596
+ if (policy?.resolve === void 0)
3597
+ return null;
3598
+ const session = exec?.agent?.session;
3599
+ try {
3600
+ return policy.resolve({ ...session === void 0 ? {} : { session } })?.mode ?? null;
3601
+ } catch {
3602
+ return null;
3603
+ }
3604
+ }
3594
3605
  var TOOL_SPECS = {
3595
3606
  status: {
3596
3607
  endpoint: "status",
@@ -3663,17 +3674,20 @@ function registerOne(ctx, service, name2) {
3663
3674
  },
3664
3675
  async execute(args, exec) {
3665
3676
  const input = args ?? {};
3666
- if (mutating) {
3677
+ const mode = sandboxMode(ctx, exec);
3678
+ const fullAccess = mode === "danger-full-access";
3679
+ if (mutating && !fullAccess) {
3667
3680
  const approval = ctx.get("approval");
3681
+ const remedy = "Set the session to Full access (danger-full-access), or use a session where approvals can be answered.";
3668
3682
  if (approval?.request === void 0)
3669
- return "refused: this deployment has no approval service, so a mutating home-hosted tool cannot run.";
3683
+ return `refused: this session runs in ${mode ?? "an unknown"} sandbox and the deployment has no approval service. ${remedy}`;
3670
3684
  const outcome = await approval.request({
3671
3685
  agent: exec?.agent,
3672
3686
  toolName,
3673
3687
  reason: `${spec.description} (${JSON.stringify(input)})`
3674
3688
  });
3675
3689
  if (outcome !== "allowed-once")
3676
- return `refused: approval answered "${outcome}".`;
3690
+ return `refused: approval answered "${outcome}" (session sandbox: ${mode ?? "unknown"}). ${remedy}`;
3677
3691
  }
3678
3692
  try {
3679
3693
  const payload = name2 === "servers_create" ? { entry: input.entry } : name2 === "servers_update" ? { id: input.id, patch: input.patch } : name2 === "status" || name2 === "servers_list" ? {} : { id: input.id, mechanism: input.mechanism };