@vitos-pizza/vitos-pizza 0.1.0 → 0.2.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.
Files changed (259) hide show
  1. package/AGENTS.md +158 -158
  2. package/CHANGELOG.md +42 -24
  3. package/LICENSE +21 -21
  4. package/README.md +237 -234
  5. package/assets/logo.svg +28 -28
  6. package/node_modules/@vitos-pizza/agent-mode/extensions/index.ts +138 -117
  7. package/node_modules/@vitos-pizza/agent-mode/package.json +3 -3
  8. package/node_modules/@vitos-pizza/agent-mode/src/apply-mode.ts +60 -60
  9. package/node_modules/@vitos-pizza/agent-mode/src/mode-events.ts +102 -102
  10. package/node_modules/@vitos-pizza/agent-mode/src/modes.ts +43 -43
  11. package/node_modules/@vitos-pizza/agent-mode/src/plan-instructions.ts +85 -44
  12. package/node_modules/@vitos-pizza/agent-mode/tests/apply-mode.test.ts +36 -36
  13. package/node_modules/@vitos-pizza/agent-mode/tests/mode-events.test.ts +22 -22
  14. package/node_modules/@vitos-pizza/agent-mode/tests/modes.test.ts +38 -38
  15. package/node_modules/@vitos-pizza/agent-mode/tests/plan-instructions.test.ts +68 -45
  16. package/node_modules/@vitos-pizza/agent-mode/tsconfig.json +13 -13
  17. package/node_modules/@vitos-pizza/agent-mode/vitest.config.ts +8 -8
  18. package/node_modules/@vitos-pizza/keybindings/extensions/index.ts +104 -104
  19. package/node_modules/@vitos-pizza/keybindings/package.json +1 -1
  20. package/node_modules/@vitos-pizza/keybindings/presets/shortcuts.json +3 -3
  21. package/node_modules/@vitos-pizza/keybindings/src/bind.ts +57 -57
  22. package/node_modules/@vitos-pizza/keybindings/src/config.ts +84 -84
  23. package/node_modules/@vitos-pizza/keybindings/src/registry.ts +43 -43
  24. package/node_modules/@vitos-pizza/keybindings/src/types.ts +54 -54
  25. package/node_modules/@vitos-pizza/keybindings/tests/config.test.ts +83 -83
  26. package/node_modules/@vitos-pizza/keybindings/tests/registry.test.ts +142 -142
  27. package/node_modules/@vitos-pizza/keybindings/tsconfig.json +13 -13
  28. package/node_modules/@vitos-pizza/keybindings/vitest.config.ts +8 -8
  29. package/node_modules/@vitos-pizza/permission-system/extensions/index.ts +162 -162
  30. package/node_modules/@vitos-pizza/permission-system/package.json +2 -2
  31. package/node_modules/@vitos-pizza/permission-system/src/bash/matcher.ts +54 -51
  32. package/node_modules/@vitos-pizza/permission-system/src/bash/parser.ts +40 -40
  33. package/node_modules/@vitos-pizza/permission-system/src/config-paths.ts +23 -23
  34. package/node_modules/@vitos-pizza/permission-system/src/detect-preset-mode.ts +61 -61
  35. package/node_modules/@vitos-pizza/permission-system/src/expand-home.ts +12 -12
  36. package/node_modules/@vitos-pizza/permission-system/src/forwarding/forwarder.ts +224 -224
  37. package/node_modules/@vitos-pizza/permission-system/src/forwarding/subagent-registry.ts +61 -61
  38. package/node_modules/@vitos-pizza/permission-system/src/gates/path-utils.ts +66 -66
  39. package/node_modules/@vitos-pizza/permission-system/src/gates/pipeline.ts +151 -155
  40. package/node_modules/@vitos-pizza/permission-system/src/handlers/before-agent-start.ts +86 -65
  41. package/node_modules/@vitos-pizza/permission-system/src/handlers/skill-input.ts +1 -1
  42. package/node_modules/@vitos-pizza/permission-system/src/handlers/tool-call-gate.ts +204 -199
  43. package/node_modules/@vitos-pizza/permission-system/src/mode-api.ts +44 -44
  44. package/node_modules/@vitos-pizza/permission-system/src/permission-evaluator.ts +233 -216
  45. package/node_modules/@vitos-pizza/permission-system/src/permission-session.ts +62 -62
  46. package/node_modules/@vitos-pizza/permission-system/src/policy-loader.ts +211 -211
  47. package/node_modules/@vitos-pizza/permission-system/src/sanitizers/skill-prompt.ts +99 -99
  48. package/node_modules/@vitos-pizza/permission-system/src/sanitizers/system-prompt.ts +221 -221
  49. package/node_modules/@vitos-pizza/permission-system/src/service.ts +66 -66
  50. package/node_modules/@vitos-pizza/permission-system/src/session-approvals.ts +4 -1
  51. package/node_modules/@vitos-pizza/permission-system/src/types.ts +80 -80
  52. package/node_modules/@vitos-pizza/permission-system/src/ui/approval-summary.ts +43 -43
  53. package/node_modules/@vitos-pizza/permission-system/src/ui/config-modal.ts +36 -36
  54. package/node_modules/@vitos-pizza/permission-system/src/ui/permission-dialog.ts +52 -52
  55. package/node_modules/@vitos-pizza/permission-system/src/wildcard-matcher.ts +81 -81
  56. package/node_modules/@vitos-pizza/permission-system/src/yaml-frontmatter.ts +84 -84
  57. package/node_modules/@vitos-pizza/permission-system/src/yolo/runtime-api.ts +49 -49
  58. package/node_modules/@vitos-pizza/permission-system/tests/bash-parser.test.ts +32 -32
  59. package/node_modules/@vitos-pizza/permission-system/tests/before-agent-start.test.ts +61 -0
  60. package/node_modules/@vitos-pizza/permission-system/tests/detect-preset-mode.test.ts +68 -68
  61. package/node_modules/@vitos-pizza/permission-system/tests/forwarder.test.ts +55 -55
  62. package/node_modules/@vitos-pizza/permission-system/tests/gates.test.ts +170 -61
  63. package/node_modules/@vitos-pizza/permission-system/tests/path-utils.test.ts +68 -68
  64. package/node_modules/@vitos-pizza/permission-system/tests/permission-evaluator.test.ts +82 -55
  65. package/node_modules/@vitos-pizza/permission-system/tests/policy-loader.test.ts +25 -25
  66. package/node_modules/@vitos-pizza/permission-system/tests/system-prompt-sanitizer.test.ts +22 -22
  67. package/node_modules/@vitos-pizza/permission-system/tests/wildcard-matcher.test.ts +27 -27
  68. package/node_modules/@vitos-pizza/permission-system/tsconfig.json +13 -13
  69. package/node_modules/@vitos-pizza/permission-system/vitest.config.ts +8 -8
  70. package/node_modules/@vitos-pizza/question/package.json +1 -1
  71. package/node_modules/@vitos-pizza/question/src/question-ui.ts +5 -1
  72. package/node_modules/@vitos-pizza/question/src/register-tool.ts +1 -4
  73. package/node_modules/@vitos-pizza/question/tests/question-ui-width-cache.test.ts +45 -0
  74. package/node_modules/@vitos-pizza/question/tests/register-tool-render.test.ts +48 -45
  75. package/node_modules/@vitos-pizza/session-title/extensions/index.ts +137 -137
  76. package/node_modules/@vitos-pizza/session-title/package.json +3 -3
  77. package/node_modules/@vitos-pizza/session-title/src/build-title-prompt.ts +16 -16
  78. package/node_modules/@vitos-pizza/session-title/src/extract-title-context.ts +55 -55
  79. package/node_modules/@vitos-pizza/session-title/src/fast-rules.ts +48 -48
  80. package/node_modules/@vitos-pizza/session-title/src/load-settings.ts +70 -70
  81. package/node_modules/@vitos-pizza/session-title/src/normalize-title.ts +20 -20
  82. package/node_modules/@vitos-pizza/session-title/src/parse-title-response.ts +6 -6
  83. package/node_modules/@vitos-pizza/session-title/src/resolve-title-model.ts +33 -33
  84. package/node_modules/@vitos-pizza/session-title/src/schedule-title-job.ts +53 -53
  85. package/node_modules/@vitos-pizza/session-title/src/should-schedule.ts +30 -30
  86. package/node_modules/@vitos-pizza/session-title/src/title-agent.ts +60 -60
  87. package/node_modules/@vitos-pizza/session-title/src/truncate-input.ts +10 -10
  88. package/node_modules/@vitos-pizza/session-title/src/types.ts +29 -29
  89. package/node_modules/@vitos-pizza/session-title/tests/build-title-prompt.test.ts +34 -34
  90. package/node_modules/@vitos-pizza/session-title/tests/extension.test.ts +198 -198
  91. package/node_modules/@vitos-pizza/session-title/tests/extract-title-context.test.ts +48 -48
  92. package/node_modules/@vitos-pizza/session-title/tests/fast-rules.test.ts +43 -43
  93. package/node_modules/@vitos-pizza/session-title/tests/parse-title-response.test.ts +34 -34
  94. package/node_modules/@vitos-pizza/session-title/tests/should-schedule.test.ts +30 -30
  95. package/node_modules/@vitos-pizza/session-title/tests/title-agent.test.ts +154 -154
  96. package/node_modules/@vitos-pizza/session-title/tests/truncate-input.test.ts +15 -15
  97. package/node_modules/@vitos-pizza/session-title/tsconfig.json +13 -13
  98. package/node_modules/@vitos-pizza/session-title/vitest.config.ts +8 -8
  99. package/node_modules/@vitos-pizza/subagents/agents/planner.md +46 -43
  100. package/node_modules/@vitos-pizza/subagents/agents/scout.md +40 -40
  101. package/node_modules/@vitos-pizza/subagents/agents/title.md +13 -13
  102. package/node_modules/@vitos-pizza/subagents/agents/worker.md +36 -36
  103. package/node_modules/@vitos-pizza/subagents/extensions/index.ts +164 -164
  104. package/node_modules/@vitos-pizza/subagents/package.json +1 -1
  105. package/node_modules/@vitos-pizza/subagents/skills/subagents/SKILL.md +86 -80
  106. package/node_modules/@vitos-pizza/subagents/src/agents.ts +166 -166
  107. package/node_modules/@vitos-pizza/subagents/src/async/job-tracker.ts +71 -71
  108. package/node_modules/@vitos-pizza/subagents/src/async/spawn-async.ts +220 -220
  109. package/node_modules/@vitos-pizza/subagents/src/async/storage.ts +57 -57
  110. package/node_modules/@vitos-pizza/subagents/src/env.ts +4 -4
  111. package/node_modules/@vitos-pizza/subagents/src/execute-subagent.ts +285 -285
  112. package/node_modules/@vitos-pizza/subagents/src/progress.ts +187 -187
  113. package/node_modules/@vitos-pizza/subagents/src/render-animation.ts +40 -40
  114. package/node_modules/@vitos-pizza/subagents/src/render.ts +428 -428
  115. package/node_modules/@vitos-pizza/subagents/src/rpc/channels.ts +21 -21
  116. package/node_modules/@vitos-pizza/subagents/src/rpc/client.ts +144 -144
  117. package/node_modules/@vitos-pizza/subagents/src/rpc/server.ts +11 -11
  118. package/node_modules/@vitos-pizza/subagents/src/run-agent.ts +313 -313
  119. package/node_modules/@vitos-pizza/subagents/src/runtime.ts +102 -102
  120. package/node_modules/@vitos-pizza/subagents/src/schema.ts +62 -62
  121. package/node_modules/@vitos-pizza/subagents/src/spawn.ts +104 -104
  122. package/node_modules/@vitos-pizza/subagents/src/types.ts +92 -92
  123. package/node_modules/@vitos-pizza/subagents/src/utils.ts +100 -100
  124. package/node_modules/@vitos-pizza/subagents/src/wait.ts +159 -159
  125. package/node_modules/@vitos-pizza/subagents/tests/agents.test.ts +19 -19
  126. package/node_modules/@vitos-pizza/subagents/tests/execute-subagent.test.ts +96 -96
  127. package/node_modules/@vitos-pizza/subagents/tests/extension.test.ts +42 -42
  128. package/node_modules/@vitos-pizza/subagents/tests/progress.test.ts +206 -206
  129. package/node_modules/@vitos-pizza/subagents/tests/rpc.test.ts +74 -74
  130. package/node_modules/@vitos-pizza/subagents/tsconfig.json +13 -13
  131. package/node_modules/@vitos-pizza/subagents/vitest.config.ts +8 -8
  132. package/node_modules/@vitos-pizza/ui-enhancements/package.json +1 -1
  133. package/node_modules/@vitos-pizza/ui-enhancements/src/chrome/agent-mode-badge.ts +60 -60
  134. package/node_modules/@vitos-pizza/ui-enhancements/src/chrome/border-status.ts +14 -1
  135. package/node_modules/@vitos-pizza/ui-enhancements/src/chrome/resize-recovery.ts +63 -0
  136. package/node_modules/@vitos-pizza/ui-enhancements/tests/agent-mode-badge.test.ts +35 -35
  137. package/node_modules/@vitos-pizza/ui-enhancements/tests/resize-recovery.test.ts +135 -0
  138. package/node_modules/@vitos-pizza/websearch/package.json +1 -1
  139. package/node_modules/node-addon-api/tools/conversion.js +0 -0
  140. package/node_modules/node-gyp-build/bin.js +0 -0
  141. package/node_modules/node-gyp-build/build-test.js +0 -0
  142. package/node_modules/node-gyp-build/optional.js +0 -0
  143. package/package.json +11 -9
  144. package/packages/agent-mode/extensions/index.ts +138 -117
  145. package/packages/agent-mode/package.json +3 -3
  146. package/packages/agent-mode/src/apply-mode.ts +60 -60
  147. package/packages/agent-mode/src/mode-events.ts +102 -102
  148. package/packages/agent-mode/src/modes.ts +43 -43
  149. package/packages/agent-mode/src/plan-instructions.ts +85 -44
  150. package/packages/agent-mode/tsconfig.json +13 -13
  151. package/packages/agent-mode/vitest.config.ts +8 -8
  152. package/packages/keybindings/extensions/index.ts +104 -104
  153. package/packages/keybindings/package.json +1 -1
  154. package/packages/keybindings/presets/shortcuts.json +3 -3
  155. package/packages/keybindings/src/bind.ts +57 -57
  156. package/packages/keybindings/src/config.ts +84 -84
  157. package/packages/keybindings/src/registry.ts +43 -43
  158. package/packages/keybindings/src/types.ts +54 -54
  159. package/packages/keybindings/tsconfig.json +13 -13
  160. package/packages/keybindings/vitest.config.ts +8 -8
  161. package/packages/permission-system/extensions/index.ts +162 -162
  162. package/packages/permission-system/node_modules/web-tree-sitter/debug/tree-sitter.wasm +0 -0
  163. package/packages/permission-system/node_modules/web-tree-sitter/tree-sitter.wasm +0 -0
  164. package/packages/permission-system/package.json +2 -2
  165. package/packages/permission-system/src/bash/matcher.ts +54 -51
  166. package/packages/permission-system/src/bash/parser.ts +40 -40
  167. package/packages/permission-system/src/config-paths.ts +23 -23
  168. package/packages/permission-system/src/detect-preset-mode.ts +61 -61
  169. package/packages/permission-system/src/expand-home.ts +12 -12
  170. package/packages/permission-system/src/forwarding/forwarder.ts +224 -224
  171. package/packages/permission-system/src/forwarding/subagent-registry.ts +61 -61
  172. package/packages/permission-system/src/gates/path-utils.ts +66 -66
  173. package/packages/permission-system/src/gates/pipeline.ts +151 -155
  174. package/packages/permission-system/src/handlers/before-agent-start.ts +86 -65
  175. package/packages/permission-system/src/handlers/skill-input.ts +1 -1
  176. package/packages/permission-system/src/handlers/tool-call-gate.ts +204 -199
  177. package/packages/permission-system/src/mode-api.ts +44 -44
  178. package/packages/permission-system/src/permission-evaluator.ts +233 -216
  179. package/packages/permission-system/src/permission-session.ts +62 -62
  180. package/packages/permission-system/src/policy-loader.ts +211 -211
  181. package/packages/permission-system/src/sanitizers/skill-prompt.ts +99 -99
  182. package/packages/permission-system/src/sanitizers/system-prompt.ts +221 -221
  183. package/packages/permission-system/src/service.ts +66 -66
  184. package/packages/permission-system/src/session-approvals.ts +4 -1
  185. package/packages/permission-system/src/types.ts +80 -80
  186. package/packages/permission-system/src/ui/approval-summary.ts +43 -43
  187. package/packages/permission-system/src/ui/config-modal.ts +36 -36
  188. package/packages/permission-system/src/ui/permission-dialog.ts +52 -52
  189. package/packages/permission-system/src/wildcard-matcher.ts +81 -81
  190. package/packages/permission-system/src/yaml-frontmatter.ts +84 -84
  191. package/packages/permission-system/src/yolo/runtime-api.ts +49 -49
  192. package/packages/permission-system/tsconfig.json +13 -13
  193. package/packages/permission-system/vitest.config.ts +8 -8
  194. package/packages/question/package.json +1 -1
  195. package/packages/question/src/question-ui.ts +5 -1
  196. package/packages/question/src/register-tool.ts +1 -4
  197. package/packages/session-title/extensions/index.ts +137 -137
  198. package/packages/session-title/package.json +3 -3
  199. package/packages/session-title/src/build-title-prompt.ts +16 -16
  200. package/packages/session-title/src/extract-title-context.ts +55 -55
  201. package/packages/session-title/src/fast-rules.ts +48 -48
  202. package/packages/session-title/src/load-settings.ts +70 -70
  203. package/packages/session-title/src/normalize-title.ts +20 -20
  204. package/packages/session-title/src/parse-title-response.ts +6 -6
  205. package/packages/session-title/src/resolve-title-model.ts +33 -33
  206. package/packages/session-title/src/schedule-title-job.ts +53 -53
  207. package/packages/session-title/src/should-schedule.ts +30 -30
  208. package/packages/session-title/src/title-agent.ts +60 -60
  209. package/packages/session-title/src/truncate-input.ts +10 -10
  210. package/packages/session-title/src/types.ts +29 -29
  211. package/packages/session-title/tsconfig.json +13 -13
  212. package/packages/session-title/vitest.config.ts +8 -8
  213. package/packages/subagents/agents/planner.md +46 -43
  214. package/packages/subagents/agents/scout.md +40 -40
  215. package/packages/subagents/agents/title.md +13 -13
  216. package/packages/subagents/agents/worker.md +36 -36
  217. package/packages/subagents/extensions/index.ts +164 -164
  218. package/packages/subagents/package.json +1 -1
  219. package/packages/subagents/skills/subagents/SKILL.md +86 -80
  220. package/packages/subagents/src/agents.ts +166 -166
  221. package/packages/subagents/src/async/job-tracker.ts +71 -71
  222. package/packages/subagents/src/async/spawn-async.ts +220 -220
  223. package/packages/subagents/src/async/storage.ts +57 -57
  224. package/packages/subagents/src/env.ts +4 -4
  225. package/packages/subagents/src/execute-subagent.ts +285 -285
  226. package/packages/subagents/src/progress.ts +187 -187
  227. package/packages/subagents/src/render-animation.ts +40 -40
  228. package/packages/subagents/src/render.ts +428 -428
  229. package/packages/subagents/src/rpc/channels.ts +21 -21
  230. package/packages/subagents/src/rpc/client.ts +144 -144
  231. package/packages/subagents/src/rpc/server.ts +11 -11
  232. package/packages/subagents/src/run-agent.ts +313 -313
  233. package/packages/subagents/src/runtime.ts +102 -102
  234. package/packages/subagents/src/schema.ts +62 -62
  235. package/packages/subagents/src/spawn.ts +104 -104
  236. package/packages/subagents/src/types.ts +92 -92
  237. package/packages/subagents/src/utils.ts +100 -100
  238. package/packages/subagents/src/wait.ts +159 -159
  239. package/packages/subagents/tsconfig.json +13 -13
  240. package/packages/subagents/vitest.config.ts +8 -8
  241. package/packages/todoist/extensions/index.ts +163 -0
  242. package/packages/todoist/package.json +34 -0
  243. package/packages/todoist/src/events.ts +7 -0
  244. package/packages/todoist/src/silent-render.ts +8 -0
  245. package/packages/todoist/src/state.ts +147 -0
  246. package/packages/todoist/src/tools.ts +175 -0
  247. package/packages/todoist/src/types.ts +34 -0
  248. package/packages/todoist/src/widget.ts +109 -0
  249. package/packages/todoist/tsconfig.json +13 -0
  250. package/packages/todoist/vitest.config.ts +8 -0
  251. package/packages/ui-enhancements/package.json +1 -1
  252. package/packages/ui-enhancements/src/chrome/agent-mode-badge.ts +60 -60
  253. package/packages/ui-enhancements/src/chrome/border-status.ts +14 -1
  254. package/packages/ui-enhancements/src/chrome/resize-recovery.ts +63 -0
  255. package/packages/websearch/package.json +1 -1
  256. package/scripts/sync-pi-manifest.mjs +1 -1
  257. package/packages/permission-system/node_modules/web-tree-sitter/lib/tree-sitter.cjs +0 -2988
  258. package/packages/permission-system/node_modules/web-tree-sitter/lib/tree-sitter.wasm +0 -0
  259. package/packages/permission-system/node_modules/web-tree-sitter/lib/tree-sitter.wasm.map +0 -1
@@ -1,65 +1,86 @@
1
- import type {
2
- BeforeAgentStartEventResult,
3
- ExtensionAPI,
4
- ExtensionContext,
5
- } from "@earendil-works/pi-coding-agent";
6
- import type { PermissionSession } from "../permission-session.ts";
7
- import { sanitizeSkillPromptBlocks } from "../sanitizers/skill-prompt.ts";
8
- import { sanitizeAvailableToolsSection } from "../sanitizers/system-prompt.ts";
9
-
10
- export function registerBeforeAgentStart(
11
- pi: ExtensionAPI,
12
- session: PermissionSession,
13
- ): void {
14
- pi.on("before_agent_start", async (event, ctx) => {
15
- session.refresh(ctx);
16
- const agentName = session.resolveAgentName(ctx, event.systemPrompt);
17
- session.setAgentName(agentName);
18
-
19
- const config = session.getConfig();
20
- const permission = config.permission ?? {};
21
- const activeTools = pi.getActiveTools();
22
- const allowedTools: string[] = [];
23
-
24
- for (const toolName of activeTools) {
25
- const state = session.evaluator.getToolPermission(permission, toolName, {
26
- yoloMode: config.yoloMode,
27
- sessionApprovals: session.sessionApprovals,
28
- });
29
- if (state !== "deny") allowedTools.push(toolName);
30
- }
31
-
32
- pi.setActiveTools(allowedTools);
33
-
34
- const toolPromptResult = sanitizeAvailableToolsSection(
35
- event.systemPrompt,
36
- allowedTools,
37
- );
38
- const skillPromptResult = sanitizeSkillPromptBlocks(
39
- toolPromptResult.prompt,
40
- permission,
41
- session.evaluator,
42
- { yoloMode: config.yoloMode, agentName },
43
- );
44
-
45
- if (
46
- skillPromptResult.prompt !== event.systemPrompt ||
47
- toolPromptResult.removed
48
- ) {
49
- return { systemPrompt: skillPromptResult.prompt };
50
- }
51
- return {};
52
- });
53
- }
54
-
55
- export function resolveAgentNameFromContext(
56
- _ctx: ExtensionContext,
57
- systemPrompt?: string,
58
- ): string | undefined {
59
- const match = systemPrompt
60
- ? /<active_agent>\s*([^\s<]+)/i.exec(systemPrompt)
61
- : null;
62
- return match?.[1];
63
- }
64
-
65
- export type { BeforeAgentStartEventResult };
1
+ import type {
2
+ BeforeAgentStartEventResult,
3
+ ExtensionAPI,
4
+ ExtensionContext,
5
+ } from "@earendil-works/pi-coding-agent";
6
+ import type {
7
+ EvaluateOptions,
8
+ PermissionEvaluator,
9
+ } from "../permission-evaluator.ts";
10
+ import type { PermissionSession } from "../permission-session.ts";
11
+ import { sanitizeSkillPromptBlocks } from "../sanitizers/skill-prompt.ts";
12
+ import { sanitizeAvailableToolsSection } from "../sanitizers/system-prompt.ts";
13
+ import type { FlatPermissionConfig } from "../types.ts";
14
+
15
+ export function selectAllowedToolNames(
16
+ toolNames: readonly string[],
17
+ permission: FlatPermissionConfig,
18
+ evaluator: PermissionEvaluator,
19
+ options: EvaluateOptions = {},
20
+ ): string[] {
21
+ const allowedTools: string[] = [];
22
+ for (const toolName of toolNames) {
23
+ const state = evaluator.getToolPermission(permission, toolName, options);
24
+ if (state !== "deny") allowedTools.push(toolName);
25
+ }
26
+ return allowedTools;
27
+ }
28
+
29
+ export function registerBeforeAgentStart(
30
+ pi: ExtensionAPI,
31
+ session: PermissionSession,
32
+ ): void {
33
+ pi.on("before_agent_start", async (event, ctx) => {
34
+ session.refresh(ctx);
35
+ const agentName = session.resolveAgentName(ctx, event.systemPrompt);
36
+ session.setAgentName(agentName);
37
+
38
+ const config = session.getConfig();
39
+ const permission = config.permission ?? {};
40
+ // Start from all registered tools so leaving a narrowed mode (e.g. plan)
41
+ // can expand write/edit/bash again — getActiveTools() only shrinks.
42
+ const candidateTools = pi.getAllTools().map((tool) => tool.name);
43
+ const allowedTools = selectAllowedToolNames(
44
+ candidateTools,
45
+ permission,
46
+ session.evaluator,
47
+ {
48
+ yoloMode: config.yoloMode,
49
+ sessionApprovals: session.sessionApprovals,
50
+ },
51
+ );
52
+
53
+ pi.setActiveTools(allowedTools);
54
+
55
+ const toolPromptResult = sanitizeAvailableToolsSection(
56
+ event.systemPrompt,
57
+ allowedTools,
58
+ );
59
+ const skillPromptResult = sanitizeSkillPromptBlocks(
60
+ toolPromptResult.prompt,
61
+ permission,
62
+ session.evaluator,
63
+ { yoloMode: config.yoloMode, agentName },
64
+ );
65
+
66
+ if (
67
+ skillPromptResult.prompt !== event.systemPrompt ||
68
+ toolPromptResult.removed
69
+ ) {
70
+ return { systemPrompt: skillPromptResult.prompt };
71
+ }
72
+ return {};
73
+ });
74
+ }
75
+
76
+ export function resolveAgentNameFromContext(
77
+ _ctx: ExtensionContext,
78
+ systemPrompt?: string,
79
+ ): string | undefined {
80
+ const match = systemPrompt
81
+ ? /<active_agent>\s*([^\s<]+)/i.exec(systemPrompt)
82
+ : null;
83
+ return match?.[1];
84
+ }
85
+
86
+ export type { BeforeAgentStartEventResult };
@@ -1 +1 @@
1
- export { registerSkillInputGate } from "./tool-call-gate.ts";
1
+ export { registerSkillInputGate } from "./tool-call-gate.ts";
@@ -1,199 +1,204 @@
1
- import type {
2
- ExtensionAPI,
3
- ExtensionContext,
4
- } from "@earendil-works/pi-coding-agent";
5
- import {
6
- forwardPermissionPrompt,
7
- resolveParentSessionId,
8
- } from "../forwarding/forwarder.ts";
9
- import { getSubagentSessionRegistry } from "../forwarding/subagent-registry.ts";
10
- import { runGatePipeline } from "../gates/pipeline.ts";
11
- import type { PermissionSession } from "../permission-session.ts";
12
- import { emitPermissionEvent } from "../service.ts";
13
- import type { GateContext } from "../types.ts";
14
- import {
15
- buildApprovalSummary,
16
- buildApprovalTitle,
17
- } from "../ui/approval-summary.ts";
18
- import { requestPermissionDecisionFromUi } from "../ui/permission-dialog.ts";
19
-
20
- export interface ToolGateDeps {
21
- getRegisteredTools?: () => ReadonlySet<string>;
22
- getSkillDirs?: () => readonly string[];
23
- getParentSessionId?: () => string | null;
24
- }
25
-
26
- export function registerToolCallGate(
27
- pi: ExtensionAPI,
28
- session: PermissionSession,
29
- deps: ToolGateDeps = {},
30
- ): void {
31
- pi.on("tool_call", async (event, ctx) => {
32
- session.refresh(ctx);
33
- const toolName = event.toolName;
34
- const config = session.getConfig();
35
- const permission = config.permission ?? {};
36
- const registeredTools =
37
- deps.getRegisteredTools?.() ??
38
- new Set(pi.getAllTools().map((tool) => tool.name));
39
-
40
- const gateContext: GateContext = {
41
- toolName,
42
- input: (event.input ?? {}) as Record<string, unknown>,
43
- cwd: ctx.cwd,
44
- agentName: session.resolveAgentName(ctx),
45
- };
46
-
47
- const result = runGatePipeline(permission, session.evaluator, gateContext, {
48
- yoloMode: config.yoloMode,
49
- platform: process.platform,
50
- skillDirs: deps.getSkillDirs?.() ?? [],
51
- registeredTools,
52
- });
53
-
54
- emitPermissionEvent(pi, "permissions:decision", {
55
- toolName,
56
- state: result.state,
57
- surface: result.surface,
58
- value: result.value,
59
- matchedPattern: result.matchedPattern,
60
- });
61
-
62
- if (result.state === "deny") {
63
- return {
64
- block: true,
65
- reason: result.reason ?? `Permission denied for ${toolName}`,
66
- };
67
- }
68
-
69
- if (result.state === "allow") {
70
- return;
71
- }
72
-
73
- const decision = await resolveAskDecision(
74
- pi,
75
- ctx,
76
- session,
77
- gateContext,
78
- result.matchedPattern,
79
- deps,
80
- );
81
- if (!decision.approved) {
82
- return {
83
- block: true,
84
- reason:
85
- decision.denialReason ??
86
- (decision.confirmationUnavailable
87
- ? "Permission confirmation unavailable"
88
- : "Permission denied by user"),
89
- };
90
- }
91
-
92
- if (decision.state === "approved_for_session" && result.matchedPattern) {
93
- session.sessionApprovals.add(result.surface, result.matchedPattern);
94
- }
95
- });
96
- }
97
-
98
- async function resolveAskDecision(
99
- pi: ExtensionAPI,
100
- ctx: ExtensionContext,
101
- _session: PermissionSession,
102
- gateContext: GateContext,
103
- matchedPattern: string | undefined,
104
- deps: ToolGateDeps,
105
- ) {
106
- const title = buildApprovalTitle(gateContext.toolName);
107
- const message = buildApprovalSummary(gateContext);
108
-
109
- if (ctx.hasUI) {
110
- emitPermissionEvent(pi, "permissions:ui_prompt", {
111
- toolName: gateContext.toolName,
112
- surface: gateContext.toolName,
113
- value: message,
114
- });
115
- return requestPermissionDecisionFromUi(ctx.ui, title, message, {
116
- sessionLabel: matchedPattern
117
- ? `Yes, for this session (${matchedPattern})`
118
- : undefined,
119
- });
120
- }
121
-
122
- const currentSessionId = ctx.sessionManager.getSessionId?.() ?? null;
123
- const registry = getSubagentSessionRegistry();
124
- const parentFromRegistry = currentSessionId
125
- ? registry.get(currentSessionId)?.parentSessionId
126
- : undefined;
127
- const parentSessionId =
128
- parentFromRegistry ??
129
- deps.getParentSessionId?.() ??
130
- resolveParentSessionId();
131
-
132
- if (!parentSessionId || !currentSessionId) {
133
- return {
134
- approved: false,
135
- state: "denied" as const,
136
- confirmationUnavailable: true as const,
137
- };
138
- }
139
-
140
- const forwarded = await forwardPermissionPrompt({
141
- events: pi.events,
142
- requesterSessionId: currentSessionId,
143
- targetSessionId: parentSessionId,
144
- title,
145
- message,
146
- surface: gateContext.toolName,
147
- value: message,
148
- });
149
-
150
- if (!forwarded) {
151
- return {
152
- approved: false,
153
- state: "denied" as const,
154
- confirmationUnavailable: true as const,
155
- };
156
- }
157
-
158
- if (forwarded.autoApproved) {
159
- return forwarded;
160
- }
161
-
162
- return forwarded;
163
- }
164
-
165
- export function registerSkillInputGate(
166
- pi: ExtensionAPI,
167
- session: PermissionSession,
168
- ): void {
169
- pi.on("input", async (event, ctx) => {
170
- const text = typeof event.text === "string" ? event.text.trim() : "";
171
- if (!text.startsWith("/")) return;
172
- const skillName = text.slice(1).split(/\s+/)[0]?.trim();
173
- if (!skillName) return;
174
-
175
- session.refresh(ctx);
176
- session.trackExplicitSkillInvocation(skillName);
177
- const config = session.getConfig();
178
- const result = session.evaluator.evaluateSurface(
179
- config.permission ?? {},
180
- "skill",
181
- skillName,
182
- "project",
183
- { yoloMode: config.yoloMode, sessionApprovals: session.sessionApprovals },
184
- );
185
-
186
- if (result.state === "deny") {
187
- ctx.ui.notify(`Skill "${skillName}" is not permitted`, "error");
188
- return;
189
- }
190
-
191
- if (result.state === "ask" && ctx.hasUI && !config.yoloMode) {
192
- const approved = await ctx.ui.confirm(
193
- `Load skill ${skillName}?`,
194
- `Allow loading skill "${skillName}"`,
195
- );
196
- if (!approved) return;
197
- }
198
- });
199
- }
1
+ import type {
2
+ ExtensionAPI,
3
+ ExtensionContext,
4
+ } from "@earendil-works/pi-coding-agent";
5
+ import {
6
+ forwardPermissionPrompt,
7
+ resolveParentSessionId,
8
+ } from "../forwarding/forwarder.ts";
9
+ import { getSubagentSessionRegistry } from "../forwarding/subagent-registry.ts";
10
+ import { runGatePipeline } from "../gates/pipeline.ts";
11
+ import { patternForSessionApproval } from "../permission-evaluator.ts";
12
+ import type { PermissionSession } from "../permission-session.ts";
13
+ import { emitPermissionEvent } from "../service.ts";
14
+ import type { GateContext } from "../types.ts";
15
+ import {
16
+ buildApprovalSummary,
17
+ buildApprovalTitle,
18
+ } from "../ui/approval-summary.ts";
19
+ import { requestPermissionDecisionFromUi } from "../ui/permission-dialog.ts";
20
+
21
+ export interface ToolGateDeps {
22
+ getRegisteredTools?: () => ReadonlySet<string>;
23
+ getSkillDirs?: () => readonly string[];
24
+ getParentSessionId?: () => string | null;
25
+ }
26
+
27
+ export function registerToolCallGate(
28
+ pi: ExtensionAPI,
29
+ session: PermissionSession,
30
+ deps: ToolGateDeps = {},
31
+ ): void {
32
+ pi.on("tool_call", async (event, ctx) => {
33
+ session.refresh(ctx);
34
+ const toolName = event.toolName;
35
+ const config = session.getConfig();
36
+ const permission = config.permission ?? {};
37
+ const registeredTools =
38
+ deps.getRegisteredTools?.() ??
39
+ new Set(pi.getAllTools().map((tool) => tool.name));
40
+
41
+ const gateContext: GateContext = {
42
+ toolName,
43
+ input: (event.input ?? {}) as Record<string, unknown>,
44
+ cwd: ctx.cwd,
45
+ agentName: session.resolveAgentName(ctx),
46
+ };
47
+
48
+ const result = runGatePipeline(permission, session.evaluator, gateContext, {
49
+ yoloMode: config.yoloMode,
50
+ platform: process.platform,
51
+ skillDirs: deps.getSkillDirs?.() ?? [],
52
+ registeredTools,
53
+ sessionApprovals: session.sessionApprovals,
54
+ });
55
+
56
+ emitPermissionEvent(pi, "permissions:decision", {
57
+ toolName,
58
+ state: result.state,
59
+ surface: result.surface,
60
+ value: result.value,
61
+ matchedPattern: result.matchedPattern,
62
+ });
63
+
64
+ if (result.state === "deny") {
65
+ return {
66
+ block: true,
67
+ reason: result.reason ?? `Permission denied for ${toolName}`,
68
+ };
69
+ }
70
+
71
+ if (result.state === "allow") {
72
+ return;
73
+ }
74
+
75
+ const decision = await resolveAskDecision(
76
+ pi,
77
+ ctx,
78
+ session,
79
+ gateContext,
80
+ result.matchedPattern,
81
+ deps,
82
+ );
83
+ if (!decision.approved) {
84
+ return {
85
+ block: true,
86
+ reason:
87
+ decision.denialReason ??
88
+ (decision.confirmationUnavailable
89
+ ? "Permission confirmation unavailable"
90
+ : "Permission denied by user"),
91
+ };
92
+ }
93
+
94
+ if (decision.state === "approved_for_session" && result.matchedPattern) {
95
+ session.sessionApprovals.add(
96
+ result.surface,
97
+ patternForSessionApproval(result.surface, result.matchedPattern),
98
+ );
99
+ }
100
+ });
101
+ }
102
+
103
+ async function resolveAskDecision(
104
+ pi: ExtensionAPI,
105
+ ctx: ExtensionContext,
106
+ _session: PermissionSession,
107
+ gateContext: GateContext,
108
+ matchedPattern: string | undefined,
109
+ deps: ToolGateDeps,
110
+ ) {
111
+ const title = buildApprovalTitle(gateContext.toolName);
112
+ const message = buildApprovalSummary(gateContext);
113
+
114
+ if (ctx.hasUI) {
115
+ emitPermissionEvent(pi, "permissions:ui_prompt", {
116
+ toolName: gateContext.toolName,
117
+ surface: gateContext.toolName,
118
+ value: message,
119
+ });
120
+ return requestPermissionDecisionFromUi(ctx.ui, title, message, {
121
+ sessionLabel: matchedPattern
122
+ ? `Yes, for this session (${matchedPattern})`
123
+ : undefined,
124
+ });
125
+ }
126
+
127
+ const currentSessionId = ctx.sessionManager.getSessionId?.() ?? null;
128
+ const registry = getSubagentSessionRegistry();
129
+ const parentFromRegistry = currentSessionId
130
+ ? registry.get(currentSessionId)?.parentSessionId
131
+ : undefined;
132
+ const parentSessionId =
133
+ parentFromRegistry ??
134
+ deps.getParentSessionId?.() ??
135
+ resolveParentSessionId();
136
+
137
+ if (!parentSessionId || !currentSessionId) {
138
+ return {
139
+ approved: false,
140
+ state: "denied" as const,
141
+ confirmationUnavailable: true as const,
142
+ };
143
+ }
144
+
145
+ const forwarded = await forwardPermissionPrompt({
146
+ events: pi.events,
147
+ requesterSessionId: currentSessionId,
148
+ targetSessionId: parentSessionId,
149
+ title,
150
+ message,
151
+ surface: gateContext.toolName,
152
+ value: message,
153
+ });
154
+
155
+ if (!forwarded) {
156
+ return {
157
+ approved: false,
158
+ state: "denied" as const,
159
+ confirmationUnavailable: true as const,
160
+ };
161
+ }
162
+
163
+ if (forwarded.autoApproved) {
164
+ return forwarded;
165
+ }
166
+
167
+ return forwarded;
168
+ }
169
+
170
+ export function registerSkillInputGate(
171
+ pi: ExtensionAPI,
172
+ session: PermissionSession,
173
+ ): void {
174
+ pi.on("input", async (event, ctx) => {
175
+ const text = typeof event.text === "string" ? event.text.trim() : "";
176
+ if (!text.startsWith("/")) return;
177
+ const skillName = text.slice(1).split(/\s+/)[0]?.trim();
178
+ if (!skillName) return;
179
+
180
+ session.refresh(ctx);
181
+ session.trackExplicitSkillInvocation(skillName);
182
+ const config = session.getConfig();
183
+ const result = session.evaluator.evaluateSurface(
184
+ config.permission ?? {},
185
+ "skill",
186
+ skillName,
187
+ "project",
188
+ { yoloMode: config.yoloMode, sessionApprovals: session.sessionApprovals },
189
+ );
190
+
191
+ if (result.state === "deny") {
192
+ ctx.ui.notify(`Skill "${skillName}" is not permitted`, "error");
193
+ return;
194
+ }
195
+
196
+ if (result.state === "ask" && ctx.hasUI && !config.yoloMode) {
197
+ const approved = await ctx.ui.confirm(
198
+ `Load skill ${skillName}?`,
199
+ `Allow loading skill "${skillName}"`,
200
+ );
201
+ if (!approved) return;
202
+ }
203
+ });
204
+ }
@@ -1,44 +1,44 @@
1
- import { existsSync } from "node:fs";
2
- import { dirname, join } from "node:path";
3
- import { fileURLToPath } from "node:url";
4
- import { getProjectConfigPath } from "./config-paths.ts";
5
- import { loadConfigFile, saveConfigFile } from "./policy-loader.ts";
6
- import type { ExtensionConfig } from "./types.ts";
7
-
8
- export type PermissionPresetName = "default" | "plan" | "yolo";
9
-
10
- export const PERMISSION_RELOAD_CONFIG_EVENT = "permission-system:reload-config";
11
-
12
- const moduleDir = dirname(fileURLToPath(import.meta.url));
13
- export const PRESETS_DIR = join(moduleDir, "..", "presets");
14
-
15
- export function loadProjectConfig(cwd: string): ExtensionConfig {
16
- const path = getProjectConfigPath(cwd);
17
- if (!existsSync(path)) {
18
- return { permission: { "*": "ask" } };
19
- }
20
- return loadConfigFile(path);
21
- }
22
-
23
- export function saveProjectConfig(
24
- cwd: string,
25
- config: ExtensionConfig,
26
- ): void {
27
- saveConfigFile(getProjectConfigPath(cwd), config);
28
- }
29
-
30
- export function applyPermissionPreset(
31
- cwd: string,
32
- preset: PermissionPresetName,
33
- ): ExtensionConfig {
34
- const presetPath = join(PRESETS_DIR, `${preset}.json`);
35
- const presetConfig = loadConfigFile(presetPath);
36
- const existing = loadProjectConfig(cwd);
37
- const next: ExtensionConfig = {
38
- ...existing,
39
- yoloMode: presetConfig.yoloMode ?? false,
40
- permission: presetConfig.permission ?? { "*": "ask" },
41
- };
42
- saveProjectConfig(cwd, next);
43
- return next;
44
- }
1
+ import { existsSync } from "node:fs";
2
+ import { dirname, join } from "node:path";
3
+ import { fileURLToPath } from "node:url";
4
+ import { getProjectConfigPath } from "./config-paths.ts";
5
+ import { loadConfigFile, saveConfigFile } from "./policy-loader.ts";
6
+ import type { ExtensionConfig } from "./types.ts";
7
+
8
+ export type PermissionPresetName = "default" | "plan" | "yolo";
9
+
10
+ export const PERMISSION_RELOAD_CONFIG_EVENT = "permission-system:reload-config";
11
+
12
+ const moduleDir = dirname(fileURLToPath(import.meta.url));
13
+ export const PRESETS_DIR = join(moduleDir, "..", "presets");
14
+
15
+ export function loadProjectConfig(cwd: string): ExtensionConfig {
16
+ const path = getProjectConfigPath(cwd);
17
+ if (!existsSync(path)) {
18
+ return { permission: { "*": "ask" } };
19
+ }
20
+ return loadConfigFile(path);
21
+ }
22
+
23
+ export function saveProjectConfig(
24
+ cwd: string,
25
+ config: ExtensionConfig,
26
+ ): void {
27
+ saveConfigFile(getProjectConfigPath(cwd), config);
28
+ }
29
+
30
+ export function applyPermissionPreset(
31
+ cwd: string,
32
+ preset: PermissionPresetName,
33
+ ): ExtensionConfig {
34
+ const presetPath = join(PRESETS_DIR, `${preset}.json`);
35
+ const presetConfig = loadConfigFile(presetPath);
36
+ const existing = loadProjectConfig(cwd);
37
+ const next: ExtensionConfig = {
38
+ ...existing,
39
+ yoloMode: presetConfig.yoloMode ?? false,
40
+ permission: presetConfig.permission ?? { "*": "ask" },
41
+ };
42
+ saveProjectConfig(cwd, next);
43
+ return next;
44
+ }