@pushary/agent-hooks 0.58.0 → 0.60.0

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.
@@ -17,14 +17,14 @@ import {
17
17
  reportEvent,
18
18
  resolvePolicy,
19
19
  waitForAnswer
20
- } from "../chunk-XG3C4DZA.js";
20
+ } from "../chunk-WNRYRN2R.js";
21
21
  import {
22
22
  getMachineId
23
23
  } from "../chunk-RN3NOEJF.js";
24
24
  import "../chunk-DWED7BS3.js";
25
25
  import {
26
26
  redactSecrets
27
- } from "../chunk-SYAAGYLZ.js";
27
+ } from "../chunk-DQAN3JQP.js";
28
28
  import {
29
29
  getApiKey,
30
30
  getBaseUrl
@@ -6,15 +6,15 @@ import {
6
6
  removeCodexHooks,
7
7
  removeGeminiSettings,
8
8
  removeInstructionBlock
9
- } from "../chunk-GSERIC6Q.js";
9
+ } from "../chunk-BQLCELYC.js";
10
10
  import {
11
11
  execNpm
12
12
  } from "../chunk-J7JWI3KU.js";
13
13
  import {
14
14
  removeClaudeMcpServers,
15
15
  removePusharySettings
16
- } from "../chunk-QYQO6RHJ.js";
17
- import "../chunk-SYAAGYLZ.js";
16
+ } from "../chunk-CAJZAFVS.js";
17
+ import "../chunk-DQAN3JQP.js";
18
18
 
19
19
  // bin/pushary-clean.ts
20
20
  import { existsSync, readFileSync, writeFileSync, rmSync, readdirSync } from "fs";
@@ -31,21 +31,21 @@ import {
31
31
  toCodexWire,
32
32
  toPolicyLookup,
33
33
  waitForAnswer
34
- } from "../chunk-XG3C4DZA.js";
34
+ } from "../chunk-WNRYRN2R.js";
35
35
  import {
36
36
  getMachineId
37
37
  } from "../chunk-RN3NOEJF.js";
38
38
  import {
39
39
  isGatingMoment,
40
40
  recordKeylessMoment
41
- } from "../chunk-5TECIV6T.js";
41
+ } from "../chunk-WLGEY4UX.js";
42
42
  import "../chunk-DWED7BS3.js";
43
43
  import {
44
44
  DECISION_LINE_MAX,
45
45
  effectiveWaitSeconds,
46
46
  hookWaitClamped,
47
47
  hookWaitDeadline
48
- } from "../chunk-SYAAGYLZ.js";
48
+ } from "../chunk-DQAN3JQP.js";
49
49
  import {
50
50
  getApiKey
51
51
  } from "../chunk-NKXSILEW.js";
@@ -3,12 +3,12 @@ import {
3
3
  askUser,
4
4
  reportEvent,
5
5
  waitForAnswer
6
- } from "../chunk-XG3C4DZA.js";
6
+ } from "../chunk-WNRYRN2R.js";
7
7
  import {
8
8
  getMachineId
9
9
  } from "../chunk-RN3NOEJF.js";
10
10
  import "../chunk-DWED7BS3.js";
11
- import "../chunk-SYAAGYLZ.js";
11
+ import "../chunk-DQAN3JQP.js";
12
12
  import {
13
13
  getApiKey
14
14
  } from "../chunk-NKXSILEW.js";
@@ -11,18 +11,18 @@ import {
11
11
  missingGeminiHookEvents,
12
12
  readCodexMcpAuth,
13
13
  untrustedCodexHookEvents
14
- } from "../chunk-GSERIC6Q.js";
14
+ } from "../chunk-BQLCELYC.js";
15
15
  import {
16
16
  execNpm
17
17
  } from "../chunk-J7JWI3KU.js";
18
18
  import {
19
19
  readLedgerSummary
20
- } from "../chunk-5TECIV6T.js";
20
+ } from "../chunk-WLGEY4UX.js";
21
21
  import {
22
22
  callMcpTool,
23
23
  sendMcpRequest
24
24
  } from "../chunk-DWED7BS3.js";
25
- import "../chunk-SYAAGYLZ.js";
25
+ import "../chunk-DQAN3JQP.js";
26
26
  import {
27
27
  getBaseUrl
28
28
  } from "../chunk-NKXSILEW.js";
@@ -23,21 +23,21 @@ import {
23
23
  savePendingQuestion,
24
24
  sendNotification,
25
25
  waitForAnswer
26
- } from "../chunk-XG3C4DZA.js";
26
+ } from "../chunk-WNRYRN2R.js";
27
27
  import {
28
28
  getMachineId
29
29
  } from "../chunk-RN3NOEJF.js";
30
30
  import {
31
31
  isGatingMoment,
32
32
  recordKeylessMoment
33
- } from "../chunk-5TECIV6T.js";
33
+ } from "../chunk-WLGEY4UX.js";
34
34
  import "../chunk-DWED7BS3.js";
35
35
  import {
36
36
  DECISION_LINE_MAX,
37
37
  effectiveWaitSeconds,
38
38
  hookWaitClamped,
39
39
  hookWaitDeadline
40
- } from "../chunk-SYAAGYLZ.js";
40
+ } from "../chunk-DQAN3JQP.js";
41
41
  import {
42
42
  getApiKey
43
43
  } from "../chunk-NKXSILEW.js";
@@ -1,14 +1,14 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  handlePreToolUse
4
- } from "../chunk-Q7VUYLH4.js";
5
- import "../chunk-QYQO6RHJ.js";
4
+ } from "../chunk-BE5X3WXL.js";
5
+ import "../chunk-CAJZAFVS.js";
6
6
  import "../chunk-YHG74UFF.js";
7
- import "../chunk-XG3C4DZA.js";
7
+ import "../chunk-WNRYRN2R.js";
8
8
  import "../chunk-RN3NOEJF.js";
9
- import "../chunk-5TECIV6T.js";
9
+ import "../chunk-WLGEY4UX.js";
10
10
  import "../chunk-DWED7BS3.js";
11
- import "../chunk-SYAAGYLZ.js";
11
+ import "../chunk-DQAN3JQP.js";
12
12
  import "../chunk-NKXSILEW.js";
13
13
 
14
14
  // bin/pushary-hook.ts
@@ -1,10 +1,10 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  handleNotification
4
- } from "../chunk-XG3C4DZA.js";
4
+ } from "../chunk-WNRYRN2R.js";
5
5
  import "../chunk-RN3NOEJF.js";
6
6
  import "../chunk-DWED7BS3.js";
7
- import "../chunk-SYAAGYLZ.js";
7
+ import "../chunk-DQAN3JQP.js";
8
8
  import "../chunk-NKXSILEW.js";
9
9
 
10
10
  // bin/pushary-notification-hook.ts
@@ -1,14 +1,14 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  handlePermissionDenied
4
- } from "../chunk-Q7VUYLH4.js";
5
- import "../chunk-QYQO6RHJ.js";
4
+ } from "../chunk-BE5X3WXL.js";
5
+ import "../chunk-CAJZAFVS.js";
6
6
  import "../chunk-YHG74UFF.js";
7
- import "../chunk-XG3C4DZA.js";
7
+ import "../chunk-WNRYRN2R.js";
8
8
  import "../chunk-RN3NOEJF.js";
9
- import "../chunk-5TECIV6T.js";
9
+ import "../chunk-WLGEY4UX.js";
10
10
  import "../chunk-DWED7BS3.js";
11
- import "../chunk-SYAAGYLZ.js";
11
+ import "../chunk-DQAN3JQP.js";
12
12
  import "../chunk-NKXSILEW.js";
13
13
 
14
14
  // bin/pushary-permission-denied-hook.ts
@@ -1,14 +1,14 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  handlePermissionRequest
4
- } from "../chunk-Q7VUYLH4.js";
5
- import "../chunk-QYQO6RHJ.js";
4
+ } from "../chunk-BE5X3WXL.js";
5
+ import "../chunk-CAJZAFVS.js";
6
6
  import "../chunk-YHG74UFF.js";
7
- import "../chunk-XG3C4DZA.js";
7
+ import "../chunk-WNRYRN2R.js";
8
8
  import "../chunk-RN3NOEJF.js";
9
- import "../chunk-5TECIV6T.js";
9
+ import "../chunk-WLGEY4UX.js";
10
10
  import "../chunk-DWED7BS3.js";
11
- import "../chunk-SYAAGYLZ.js";
11
+ import "../chunk-DQAN3JQP.js";
12
12
  import "../chunk-NKXSILEW.js";
13
13
 
14
14
  // bin/pushary-permission-hook.ts
@@ -1,10 +1,10 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  handlePostToolUse
4
- } from "../chunk-XG3C4DZA.js";
4
+ } from "../chunk-WNRYRN2R.js";
5
5
  import "../chunk-RN3NOEJF.js";
6
6
  import "../chunk-DWED7BS3.js";
7
- import "../chunk-SYAAGYLZ.js";
7
+ import "../chunk-DQAN3JQP.js";
8
8
  import "../chunk-NKXSILEW.js";
9
9
 
10
10
  // bin/pushary-post-hook.ts
@@ -1,10 +1,10 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  handleUserPrompt
4
- } from "../chunk-XG3C4DZA.js";
4
+ } from "../chunk-WNRYRN2R.js";
5
5
  import "../chunk-RN3NOEJF.js";
6
6
  import "../chunk-DWED7BS3.js";
7
- import "../chunk-SYAAGYLZ.js";
7
+ import "../chunk-DQAN3JQP.js";
8
8
  import "../chunk-NKXSILEW.js";
9
9
 
10
10
  // bin/pushary-prompt-hook.ts
@@ -1,10 +1,10 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  handleSessionEnd
4
- } from "../chunk-XG3C4DZA.js";
4
+ } from "../chunk-WNRYRN2R.js";
5
5
  import "../chunk-RN3NOEJF.js";
6
6
  import "../chunk-DWED7BS3.js";
7
- import "../chunk-SYAAGYLZ.js";
7
+ import "../chunk-DQAN3JQP.js";
8
8
  import "../chunk-NKXSILEW.js";
9
9
 
10
10
  // bin/pushary-session-end-hook.ts
@@ -1,10 +1,10 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  handleSessionStart
4
- } from "../chunk-XG3C4DZA.js";
4
+ } from "../chunk-WNRYRN2R.js";
5
5
  import "../chunk-RN3NOEJF.js";
6
6
  import "../chunk-DWED7BS3.js";
7
- import "../chunk-SYAAGYLZ.js";
7
+ import "../chunk-DQAN3JQP.js";
8
8
  import "../chunk-NKXSILEW.js";
9
9
 
10
10
  // bin/pushary-session-start-hook.ts
@@ -13,7 +13,7 @@ import {
13
13
  renderAgentInstructions,
14
14
  renderProjectAgentInstructions,
15
15
  writeInstructionBlock
16
- } from "../chunk-GSERIC6Q.js";
16
+ } from "../chunk-BQLCELYC.js";
17
17
  import {
18
18
  execNpm,
19
19
  npmErrorMessage,
@@ -25,15 +25,15 @@ import {
25
25
  addClaudeMcpServer,
26
26
  addPusharyHooks,
27
27
  addPusharyToolPermissions
28
- } from "../chunk-QYQO6RHJ.js";
28
+ } from "../chunk-CAJZAFVS.js";
29
29
  import {
30
30
  reportEvent
31
- } from "../chunk-XG3C4DZA.js";
31
+ } from "../chunk-WNRYRN2R.js";
32
32
  import "../chunk-RN3NOEJF.js";
33
33
  import "../chunk-DWED7BS3.js";
34
34
  import {
35
35
  isValidApiKey
36
- } from "../chunk-SYAAGYLZ.js";
36
+ } from "../chunk-DQAN3JQP.js";
37
37
  import "../chunk-NKXSILEW.js";
38
38
 
39
39
  // bin/pushary-setup.ts
@@ -1,8 +1,8 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  readLedgerSummary
4
- } from "../chunk-5TECIV6T.js";
5
- import "../chunk-SYAAGYLZ.js";
4
+ } from "../chunk-WLGEY4UX.js";
5
+ import "../chunk-DQAN3JQP.js";
6
6
  import {
7
7
  getApiKey
8
8
  } from "../chunk-NKXSILEW.js";
@@ -1,10 +1,10 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  handleStop
4
- } from "../chunk-XG3C4DZA.js";
4
+ } from "../chunk-WNRYRN2R.js";
5
5
  import "../chunk-RN3NOEJF.js";
6
6
  import "../chunk-DWED7BS3.js";
7
- import "../chunk-SYAAGYLZ.js";
7
+ import "../chunk-DQAN3JQP.js";
8
8
  import "../chunk-NKXSILEW.js";
9
9
 
10
10
  // bin/pushary-stop-hook.ts
@@ -1,10 +1,10 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  handleStopFailure
4
- } from "../chunk-XG3C4DZA.js";
4
+ } from "../chunk-WNRYRN2R.js";
5
5
  import "../chunk-RN3NOEJF.js";
6
6
  import "../chunk-DWED7BS3.js";
7
- import "../chunk-SYAAGYLZ.js";
7
+ import "../chunk-DQAN3JQP.js";
8
8
  import "../chunk-NKXSILEW.js";
9
9
 
10
10
  // bin/pushary-stopfailure-hook.ts
@@ -8,8 +8,8 @@ import {
8
8
  addClaudeMcpServer,
9
9
  addPusharyHooks,
10
10
  addPusharyToolPermissions
11
- } from "../chunk-QYQO6RHJ.js";
12
- import "../chunk-SYAAGYLZ.js";
11
+ } from "../chunk-CAJZAFVS.js";
12
+ import "../chunk-DQAN3JQP.js";
13
13
  import {
14
14
  getApiKey
15
15
  } from "../chunk-NKXSILEW.js";
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  PRETOOLUSE_HANDLED_TOOLS
3
- } from "./chunk-QYQO6RHJ.js";
3
+ } from "./chunk-CAJZAFVS.js";
4
4
  import {
5
5
  denyReasonFrom,
6
6
  isDeferAnswer
@@ -21,23 +21,27 @@ import {
21
21
  repoKeyFor,
22
22
  resolvePolicy,
23
23
  savePendingQuestion,
24
+ scopePathFor,
24
25
  sendNotification,
25
26
  throttlePass,
26
27
  waitForAnswer
27
- } from "./chunk-XG3C4DZA.js";
28
+ } from "./chunk-WNRYRN2R.js";
28
29
  import {
29
30
  getMachineId
30
31
  } from "./chunk-RN3NOEJF.js";
31
32
  import {
32
33
  isGatingMoment,
33
34
  recordKeylessMoment
34
- } from "./chunk-5TECIV6T.js";
35
+ } from "./chunk-WLGEY4UX.js";
35
36
  import {
36
37
  buildDecisionEpisodeFeatures,
37
38
  effectiveWaitSeconds,
39
+ evaluateScope,
38
40
  hookWaitClamped,
39
- hookWaitDeadline
40
- } from "./chunk-SYAAGYLZ.js";
41
+ hookWaitDeadline,
42
+ scopeChangeReason,
43
+ scopeRequiresHuman
44
+ } from "./chunk-DQAN3JQP.js";
41
45
  import {
42
46
  getApiKey,
43
47
  getBaseUrl
@@ -257,7 +261,7 @@ var emitDecisionEpisode = (apiKey, input, toolPolicy, machineId, toolTarget, dec
257
261
  machineId
258
262
  });
259
263
  };
260
- var dispatchModeHandler = async (apiKey, input, toolPolicy, machineId, trainingConsent = false) => {
264
+ var dispatchModeHandler = async (apiKey, input, toolPolicy, machineId, trainingConsent = false, scopeReason, scopePath) => {
261
265
  const description = describeToolCall(input.tool_name, input.tool_input, "hook");
262
266
  const projectName = basename(input.cwd ?? process.cwd());
263
267
  const sessionId = input.session_id;
@@ -266,8 +270,11 @@ var dispatchModeHandler = async (apiKey, input, toolPolicy, machineId, trainingC
266
270
  const decision = {
267
271
  intent,
268
272
  action: deriveAction(input.tool_name, input.tool_input),
269
- blocker: deriveBlocker(toolPolicy.mode),
270
- actionBody: deriveActionBody(input.tool_name, input.tool_input)
273
+ blocker: scopeReason ?? deriveBlocker(toolPolicy.mode),
274
+ actionBody: deriveActionBody(input.tool_name, input.tool_input),
275
+ // Only when the breach is what caused the question, so an ordinary approval
276
+ // can never silently widen a scope.
277
+ scopePath: scopeReason ? scopePath : void 0
271
278
  };
272
279
  const signals = {};
273
280
  const runHandler = () => {
@@ -397,13 +404,16 @@ var handlePreToolUse = async (input) => {
397
404
  return void 0;
398
405
  }
399
406
  const toolPolicy = resolvePolicy(policy, input.tool_name, modeState.mode, input.tool_input, input.cwd, repoKeyFor(input.cwd));
407
+ const scopePath = modeState.scope ? scopePathFor(input.tool_name, input.tool_input, input.cwd) : void 0;
408
+ const scopeVerdict = evaluateScope(modeState.scope, scopePath);
409
+ const scopeReason = scopeChangeReason(scopeVerdict);
400
410
  if (toolPolicy.timeoutSeconds === 0 && toolPolicy.timeoutAction === "approve") {
401
- return allow();
411
+ if (!scopeRequiresHuman(scopeVerdict)) return allow();
402
412
  }
403
413
  if (toolPolicy.timeoutSeconds === 0 && toolPolicy.timeoutAction === "deny") {
404
414
  return deny(`Denied by policy for ${toolPolicy.tool}`);
405
415
  }
406
- return dispatchModeHandler(apiKey, input, toolPolicy, getMachineId(), modeState.trainingConsent);
416
+ return dispatchModeHandler(apiKey, input, toolPolicy, getMachineId(), modeState.trainingConsent, scopeReason, scopePath);
407
417
  } catch {
408
418
  return void 0;
409
419
  }
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  HOOK_BUDGETS
3
- } from "./chunk-SYAAGYLZ.js";
3
+ } from "./chunk-DQAN3JQP.js";
4
4
 
5
5
  // src/codex-config.ts
6
6
  import { createHash } from "crypto";
@@ -248,6 +248,7 @@ You have Pushary MCP tools that reach the user on their phone. Use them proactiv
248
248
 
249
249
  - When you need the user to make a decision or answer a clarifying question, call \`ask_user\` instead of guessing or stalling. Use type "confirm" for yes/no, "select" for a fixed set of options, and "input" for free text. \`ask_user\` blocks until the user replies or it times out, so you do not need a separate wait step.
250
250
  - Before any risky or irreversible step (deleting files, force pushing, spending money, sending external messages), call \`ask_user\` with type "confirm" and wait for approval.
251
+ - Before starting a multi-step run that will change several files, call \`propose_scope\` once with the paths you expect to change, anything you promise not to touch, and what done means. The user agrees to the boundary in one tap, and after that only work outside it needs approving. Skip it for a single quick edit, and do not propose a new scope mid-run just to widen one: if you need to go outside the agreed scope, do the work and let the approval that follows widen it.
251
252
  - When you are blocked, stuck, or hit an error you cannot resolve on your own, call \`send_notification\` so the user knows, and call \`ask_user\` if you need a decision to continue.
252
253
  - When a task that took several steps finishes, call \`send_notification\` so the user knows it is done.
253
254
  - Use \`cancel_question\` to retract a pending question once it is no longer needed.
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  HOOK_BUDGETS
3
- } from "./chunk-SYAAGYLZ.js";
3
+ } from "./chunk-DQAN3JQP.js";
4
4
 
5
5
  // src/claude-config.ts
6
6
  import { join } from "path";
@@ -724,6 +724,39 @@ var buildDecisionEpisodeFeatures = (input) => {
724
724
  agentType: input.agentType
725
725
  };
726
726
  };
727
+ var matchesGlobPath = (glob, path) => {
728
+ if (glob === path) return true;
729
+ const compiled = compiledGlob(glob);
730
+ return compiled ? matchGlobTokens(compiled, path) : false;
731
+ };
732
+ var evaluateScope = (contract, path) => {
733
+ if (!contract) return { outcome: "no_contract" };
734
+ if (typeof path !== "string" || path.length === 0) return { outcome: "in_scope" };
735
+ for (const rule of contract.offLimitsPaths) {
736
+ if (matchesGlobPath(rule, path)) return { outcome: "off_limits", path, rule };
737
+ }
738
+ if (contract.allowedPaths.length === 0) return { outcome: "in_scope" };
739
+ for (const rule of contract.allowedPaths) {
740
+ if (matchesGlobPath(rule, path)) return { outcome: "in_scope" };
741
+ }
742
+ return { outcome: "out_of_scope", path };
743
+ };
744
+ var scopeRequiresHuman = (verdict) => verdict.outcome === "out_of_scope" || verdict.outcome === "off_limits";
745
+ var scopeChangeReason = (verdict) => {
746
+ if (verdict.outcome === "out_of_scope") {
747
+ return `Outside the scope you approved: ${verdict.path}`;
748
+ }
749
+ if (verdict.outcome === "off_limits") {
750
+ return `You marked this off limits: ${verdict.path} (${verdict.rule})`;
751
+ }
752
+ return void 0;
753
+ };
754
+ var isScopeContract = (value) => {
755
+ if (!value || typeof value !== "object") return false;
756
+ const c = value;
757
+ const strings = (v) => Array.isArray(v) && v.every((x) => typeof x === "string");
758
+ return strings(c.allowedPaths) && strings(c.offLimitsPaths) && typeof c.doneWhen === "string";
759
+ };
727
760
 
728
761
  export {
729
762
  isApprovalMode,
@@ -744,5 +777,9 @@ export {
744
777
  redactSecrets,
745
778
  redactSecretsDeep,
746
779
  classifyDecisionRisk,
747
- buildDecisionEpisodeFeatures
780
+ buildDecisionEpisodeFeatures,
781
+ evaluateScope,
782
+ scopeRequiresHuman,
783
+ scopeChangeReason,
784
+ isScopeContract
748
785
  };
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  isSafeReadOnlyCommand
3
- } from "./chunk-SYAAGYLZ.js";
3
+ } from "./chunk-DQAN3JQP.js";
4
4
 
5
5
  // src/ledger.ts
6
6
  import { appendFileSync, existsSync, mkdirSync, readFileSync, renameSync, rmSync, statSync } from "fs";
@@ -12,6 +12,7 @@ import {
12
12
  isApprovalMode,
13
13
  isRepoKeyMatch,
14
14
  isSafeReadOnlyCommand,
15
+ isScopeContract,
15
16
  localRepoKey,
16
17
  matchRankWeight,
17
18
  matchToolPatternAny,
@@ -19,7 +20,7 @@ import {
19
20
  policyArgForms,
20
21
  redactSecrets,
21
22
  redactSecretsDeep
22
- } from "./chunk-SYAAGYLZ.js";
23
+ } from "./chunk-DQAN3JQP.js";
23
24
  import {
24
25
  getApiKey,
25
26
  getBaseUrl
@@ -183,7 +184,7 @@ var fetchModeState = async (apiKey, sessionId) => {
183
184
  headers: { "Authorization": `Bearer ${apiKey}` },
184
185
  signal: AbortSignal.timeout(3e3)
185
186
  });
186
- if (!response.ok) return { mode: null, kill: false, policyVersion: null, relayUrl: null, trainingConsent: false };
187
+ if (!response.ok) return { mode: null, kill: false, policyVersion: null, relayUrl: null, trainingConsent: false, scope: null };
187
188
  const data = await response.json();
188
189
  const mode = data.override?.mode;
189
190
  return {
@@ -191,10 +192,13 @@ var fetchModeState = async (apiKey, sessionId) => {
191
192
  kill: data.kill === true,
192
193
  policyVersion: toPolicyVersion(data.policyVersion),
193
194
  relayUrl: typeof data.relayUrl === "string" && data.relayUrl.length > 0 ? data.relayUrl : null,
194
- trainingConsent: data.trainingConsent === true
195
+ trainingConsent: data.trainingConsent === true,
196
+ // Validated rather than cast: a malformed contract must read as no contract,
197
+ // never as one that silently matches nothing.
198
+ scope: isScopeContract(data.scope) ? data.scope : null
195
199
  };
196
200
  } catch {
197
- return { mode: null, kill: false, policyVersion: null, relayUrl: null, trainingConsent: false };
201
+ return { mode: null, kill: false, policyVersion: null, relayUrl: null, trainingConsent: false, scope: null };
198
202
  }
199
203
  };
200
204
  var fetchModeOverride = async (apiKey) => (await fetchModeState(apiKey)).mode;
@@ -345,6 +349,11 @@ var deriveReceiptMeta = (toolName, toolInput, toolResult, cwd) => {
345
349
  return void 0;
346
350
  }
347
351
  };
352
+ var scopePathFor = (toolName, toolInput, cwd) => {
353
+ const meta = deriveReceiptMeta(toolName, toolInput, void 0, cwd);
354
+ if (!meta) return void 0;
355
+ return meta.kind === "edit" || meta.kind === "write" ? meta.target : void 0;
356
+ };
348
357
  var firstString = (...values) => values.find((v) => typeof v === "string" && v.length > 0);
349
358
  var ACTION_BODY_TRUNCATION_MARKER = "\n\u2026 [truncated]";
350
359
  var capActionBody = (text) => text.length <= ACTION_BODY_MAX ? text : `${text.slice(0, ACTION_BODY_MAX - ACTION_BODY_TRUNCATION_MARKER.length)}${ACTION_BODY_TRUNCATION_MARKER}`;
@@ -1231,6 +1240,7 @@ export {
1231
1240
  sendNotification,
1232
1241
  describeToolCall,
1233
1242
  deriveToolTarget,
1243
+ scopePathFor,
1234
1244
  deriveActionBody,
1235
1245
  deriveAction,
1236
1246
  deriveBlocker,
@@ -1,4 +1,4 @@
1
- import { PolicyConfig, ApprovalMode, ToolPolicy } from '@pushary/contracts';
1
+ import { PolicyConfig, ApprovalMode, ToolPolicy, ScopeContract } from '@pushary/contracts';
2
2
  export { ApprovalMode, PolicyConfig, ToolPolicy } from '@pushary/contracts';
3
3
 
4
4
  interface ToolInput {
@@ -35,6 +35,7 @@ interface ModeState {
35
35
  readonly policyVersion: string | null;
36
36
  readonly relayUrl: string | null;
37
37
  readonly trainingConsent?: boolean;
38
+ readonly scope?: ScopeContract | null;
38
39
  }
39
40
  declare const fetchModeState: (apiKey: string, sessionId?: string) => Promise<ModeState>;
40
41
  declare const fetchModeOverride: (apiKey: string) => Promise<ApprovalMode | null>;
@@ -123,6 +124,7 @@ interface AskUserParams {
123
124
  action?: string;
124
125
  blocker?: string;
125
126
  actionBody?: string;
127
+ scopePath?: string;
126
128
  }
127
129
  interface AskUserResponse {
128
130
  correlationId: string;
package/dist/src/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  handlePreToolUse
3
- } from "../chunk-Q7VUYLH4.js";
4
- import "../chunk-QYQO6RHJ.js";
3
+ } from "../chunk-BE5X3WXL.js";
4
+ import "../chunk-CAJZAFVS.js";
5
5
  import "../chunk-YHG74UFF.js";
6
6
  import {
7
7
  askUser,
@@ -15,11 +15,11 @@ import {
15
15
  reportEvent,
16
16
  resolvePolicy,
17
17
  waitForAnswer
18
- } from "../chunk-XG3C4DZA.js";
18
+ } from "../chunk-WNRYRN2R.js";
19
19
  import "../chunk-RN3NOEJF.js";
20
- import "../chunk-5TECIV6T.js";
20
+ import "../chunk-WLGEY4UX.js";
21
21
  import "../chunk-DWED7BS3.js";
22
- import "../chunk-SYAAGYLZ.js";
22
+ import "../chunk-DQAN3JQP.js";
23
23
  import {
24
24
  getApiKey,
25
25
  getBaseUrl
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pushary/agent-hooks",
3
- "version": "0.58.0",
3
+ "version": "0.60.0",
4
4
  "description": "Permission hooks for AI coding agents: route tool approvals through Pushary push notifications",
5
5
  "keywords": [
6
6
  "pushary",