@goodandready/dsh-approval-gate 0.1.9 → 0.1.10

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,11 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.1.10
4
+
5
+ - Policy: точное определение сессионной политики апрува в `readKnobs` через `approval.effectivePolicy(session)`, историю событий `session.eventAt` и пресет `danger-full-access` (Refs: #140).
6
+ - Execution: корректный пропуск некритичных команд в режиме «Полный доступ» (`never`) без ложного поднятия `ask` и автоматического отказа DSH `Error: the user rejected tool` (Refs: #140).
7
+ - Observability: форматирование `sessionTag` с получением строкового идентификатора сессии `session.id` / `session.name` вместо `[object Object]` (Refs: #140).
8
+
3
9
  ## 0.1.9
4
10
 
5
11
  - Inspect: декомпозиция `lib/inspect.js` с выносом `checkEnvExfiltration` в `lib/inspect-env.js`, строгое соблюдение порога 600 строк (Refs: #35).
package/lib/index.js CHANGED
@@ -44,8 +44,15 @@ export function isUnattended(knobs) {
44
44
  return policies.includes(policy);
45
45
  }
46
46
 
47
- /** Effective permission knobs, as the harness exposes them. */
48
- function readKnobs(ctx) {
47
+ /**
48
+ * Effective permission knobs, as the harness exposes them.
49
+ * Resolves session-level approval policy if an active execution carrying a session is provided.
50
+ *
51
+ * @param {any} ctx - Cordis context
52
+ * @param {any} [execution] - Optional tool execution carrying agent/session
53
+ * @returns {{ policy: string, sandbox: string }}
54
+ */
55
+ export function readKnobs(ctx, execution) {
49
56
  const read = (name) => {
50
57
  try {
51
58
  return ctx && typeof ctx.get === "function" ? ctx.get(name) : undefined;
@@ -55,8 +62,84 @@ function readKnobs(ctx) {
55
62
  };
56
63
  const approval = read("approval");
57
64
  const shell = read("shell");
65
+
66
+ const agent = execution && execution.agent;
67
+ const session = agent && (agent.session || (typeof agent === "object" && typeof agent.eventAt === "function" ? agent : undefined));
68
+
69
+ let policy = "";
70
+
71
+ // 1. Live approval service effectivePolicy(session)
72
+ if (approval && session && typeof approval.effectivePolicy === "function") {
73
+ try {
74
+ policy = approval.effectivePolicy(session) || "";
75
+ } catch {
76
+ // ignore
77
+ }
78
+ }
79
+
80
+ // 2. Direct session property fallback
81
+ if (!policy && session && typeof session.approvalPolicy === "string") {
82
+ policy = session.approvalPolicy;
83
+ }
84
+
85
+ // 3. Fallback: inspect session events backwards for approval/policy
86
+ if (!policy && session && typeof session.eventAt === "function" && typeof session.seq === "number") {
87
+ try {
88
+ for (let seq = session.seq - 1; seq >= 0; seq -= 1) {
89
+ const event = session.eventAt(seq);
90
+ if (event && event.type === "approval/policy" && event.data && event.data.policy) {
91
+ policy = event.data.policy;
92
+ break;
93
+ }
94
+ }
95
+ } catch {
96
+ // ignore
97
+ }
98
+ }
99
+
100
+ // 4. Fallback: inspect session events for permission/preset === 'danger-full-access'
101
+ if (!policy && session && typeof session.eventAt === "function" && typeof session.seq === "number") {
102
+ try {
103
+ for (let seq = session.seq - 1; seq >= 0; seq -= 1) {
104
+ const event = session.eventAt(seq);
105
+ if (event && event.type === "permission/preset" && event.data && event.data.preset) {
106
+ if (event.data.preset === "danger-full-access") {
107
+ policy = "never";
108
+ }
109
+ break;
110
+ }
111
+ }
112
+ } catch {
113
+ // ignore
114
+ }
115
+ }
116
+
117
+ // 5. In-memory session.events array fallback (common in mocks / tests)
118
+ if (!policy && session && Array.isArray(session.events)) {
119
+ try {
120
+ for (let i = session.events.length - 1; i >= 0; i -= 1) {
121
+ const event = session.events[i];
122
+ if (event && event.type === "approval/policy" && event.data && event.data.policy) {
123
+ policy = event.data.policy;
124
+ break;
125
+ }
126
+ if (event && event.type === "permission/preset" && event.data && event.data.preset === "danger-full-access") {
127
+ policy = "never";
128
+ break;
129
+ }
130
+ }
131
+ } catch {
132
+ // ignore
133
+ }
134
+ }
135
+
136
+ // 6. Global static fallback from approval service config
137
+ if (!policy && approval && approval.config && approval.config.policy) {
138
+ policy = approval.config.policy;
139
+ }
140
+
58
141
  return {
59
- policy: (approval && approval.config && approval.config.policy) || "",
142
+ policy: policy || "",
60
143
  sandbox: (shell && shell.sandboxMode) || "",
61
144
  };
62
145
  }
@@ -106,7 +189,7 @@ export function apply(ctx, config) {
106
189
  const hasPreExecute = ctx && typeof ctx.on === "function";
107
190
  if (hasPreExecute) {
108
191
  ctx.on("tools/pre-execute", function (execution, next) {
109
- const knobs = readKnobs(ctx);
192
+ const knobs = readKnobs(ctx, execution);
110
193
  const who = sessionTag(execution);
111
194
  const sessionId = (execution && execution.agent && (execution.agent.session || execution.agent)) || "default";
112
195
  const decision = engine.decide(execution, { sessionId, knobs });
package/lib/messages.js CHANGED
@@ -178,7 +178,11 @@ export const MESSAGES = Object.freeze({
178
178
  export function sessionTag(execution) {
179
179
  const agent = execution && execution.agent;
180
180
  if (typeof agent === "string") return " (session " + agent + ")";
181
- if (agent && agent.session) return " (session " + String(agent.session) + ")";
181
+ if (agent && agent.session) {
182
+ const s = agent.session;
183
+ const sid = typeof s === "string" ? s : (s.id || s.name || s.sessionId || s.sessionKey);
184
+ return sid ? " (session " + String(sid) + ")" : "";
185
+ }
182
186
  return "";
183
187
  }
184
188
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@goodandready/dsh-approval-gate",
3
- "version": "0.1.9",
3
+ "version": "0.1.10",
4
4
  "description": "Host-only command safety gate for DeepSeek Harness: blocks recognized dangerous operations and requests DSH approval when a command cannot be inspected.",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",