@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,61 +1,61 @@
1
- import { existsSync, readdirSync, readFileSync } from "node:fs";
2
- import { join } from "node:path";
3
- import type { FlatPermissionConfig } from "./types.ts";
4
-
5
- const PRESET_ORDER = ["yolo", "default", "plan"] as const;
6
- export type PresetModeName = (typeof PRESET_ORDER)[number];
7
-
8
- function canonicalize(value: unknown): unknown {
9
- if (Array.isArray(value)) return value.map(canonicalize);
10
- if (value && typeof value === "object") {
11
- const record = value as Record<string, unknown>;
12
- const sorted: Record<string, unknown> = {};
13
- for (const key of Object.keys(record).sort()) {
14
- sorted[key] = canonicalize(record[key]);
15
- }
16
- return sorted;
17
- }
18
- return value;
19
- }
20
-
21
- function normalizePermission(
22
- permission: FlatPermissionConfig | undefined,
23
- ): string {
24
- return JSON.stringify(canonicalize(permission ?? {}));
25
- }
26
-
27
- export function detectPresetMode(
28
- permission: FlatPermissionConfig | undefined,
29
- presetsDir: string,
30
- ): PresetModeName | "custom" {
31
- if (!existsSync(presetsDir)) return "custom";
32
-
33
- const target = normalizePermission(permission);
34
- for (const mode of PRESET_ORDER) {
35
- const presetPath = join(presetsDir, `${mode}.json`);
36
- if (!existsSync(presetPath)) continue;
37
- try {
38
- const preset = JSON.parse(readFileSync(presetPath, "utf8")) as {
39
- permission?: FlatPermissionConfig;
40
- };
41
- if (normalizePermission(preset.permission) === target) {
42
- return mode;
43
- }
44
- } catch {
45
- // skip invalid preset
46
- }
47
- }
48
-
49
- return "custom";
50
- }
51
-
52
- export function listPresetModes(presetsDir: string): PresetModeName[] {
53
- if (!existsSync(presetsDir)) return [...PRESET_ORDER];
54
- return readdirSync(presetsDir)
55
- .filter((name) => name.endsWith(".json"))
56
- .map((name) => name.replace(/\.json$/, ""))
57
- .filter((name): name is PresetModeName =>
58
- (PRESET_ORDER as readonly string[]).includes(name),
59
- )
60
- .sort((a, b) => PRESET_ORDER.indexOf(a) - PRESET_ORDER.indexOf(b));
61
- }
1
+ import { existsSync, readdirSync, readFileSync } from "node:fs";
2
+ import { join } from "node:path";
3
+ import type { FlatPermissionConfig } from "./types.ts";
4
+
5
+ const PRESET_ORDER = ["yolo", "default", "plan"] as const;
6
+ export type PresetModeName = (typeof PRESET_ORDER)[number];
7
+
8
+ function canonicalize(value: unknown): unknown {
9
+ if (Array.isArray(value)) return value.map(canonicalize);
10
+ if (value && typeof value === "object") {
11
+ const record = value as Record<string, unknown>;
12
+ const sorted: Record<string, unknown> = {};
13
+ for (const key of Object.keys(record).sort()) {
14
+ sorted[key] = canonicalize(record[key]);
15
+ }
16
+ return sorted;
17
+ }
18
+ return value;
19
+ }
20
+
21
+ function normalizePermission(
22
+ permission: FlatPermissionConfig | undefined,
23
+ ): string {
24
+ return JSON.stringify(canonicalize(permission ?? {}));
25
+ }
26
+
27
+ export function detectPresetMode(
28
+ permission: FlatPermissionConfig | undefined,
29
+ presetsDir: string,
30
+ ): PresetModeName | "custom" {
31
+ if (!existsSync(presetsDir)) return "custom";
32
+
33
+ const target = normalizePermission(permission);
34
+ for (const mode of PRESET_ORDER) {
35
+ const presetPath = join(presetsDir, `${mode}.json`);
36
+ if (!existsSync(presetPath)) continue;
37
+ try {
38
+ const preset = JSON.parse(readFileSync(presetPath, "utf8")) as {
39
+ permission?: FlatPermissionConfig;
40
+ };
41
+ if (normalizePermission(preset.permission) === target) {
42
+ return mode;
43
+ }
44
+ } catch {
45
+ // skip invalid preset
46
+ }
47
+ }
48
+
49
+ return "custom";
50
+ }
51
+
52
+ export function listPresetModes(presetsDir: string): PresetModeName[] {
53
+ if (!existsSync(presetsDir)) return [...PRESET_ORDER];
54
+ return readdirSync(presetsDir)
55
+ .filter((name) => name.endsWith(".json"))
56
+ .map((name) => name.replace(/\.json$/, ""))
57
+ .filter((name): name is PresetModeName =>
58
+ (PRESET_ORDER as readonly string[]).includes(name),
59
+ )
60
+ .sort((a, b) => PRESET_ORDER.indexOf(a) - PRESET_ORDER.indexOf(b));
61
+ }
@@ -1,12 +1,12 @@
1
- import { homedir } from "node:os";
2
-
3
- export function expandHomePath(value: string): string {
4
- if (!value.startsWith("~/")) return value;
5
- return joinPath(homedir(), value.slice(2));
6
- }
7
-
8
- function joinPath(base: string, rest: string): string {
9
- if (!rest) return base;
10
- const sep = base.includes("\\") ? "\\" : "/";
11
- return `${base}${sep}${rest}`;
12
- }
1
+ import { homedir } from "node:os";
2
+
3
+ export function expandHomePath(value: string): string {
4
+ if (!value.startsWith("~/")) return value;
5
+ return joinPath(homedir(), value.slice(2));
6
+ }
7
+
8
+ function joinPath(base: string, rest: string): string {
9
+ if (!rest) return base;
10
+ const sep = base.includes("\\") ? "\\" : "/";
11
+ return `${base}${sep}${rest}`;
12
+ }
@@ -1,224 +1,224 @@
1
- import { randomUUID } from "node:crypto";
2
- import {
3
- mkdirSync,
4
- readdirSync,
5
- readFileSync,
6
- unlinkSync,
7
- writeFileSync,
8
- } from "node:fs";
9
- import { join } from "node:path";
10
- import type { PermissionPromptDecision } from "../ui/permission-dialog.ts";
11
-
12
- export const PERMISSION_RPC_PROMPT = "permissions:rpc:prompt";
13
- export const PERMISSION_RPC_PROMPT_REPLY_PREFIX =
14
- "permissions:rpc:prompt:reply:";
15
-
16
- export const SUBAGENT_PARENT_SESSION_ENV_CANDIDATES = [
17
- "PI_AGENT_ROUTER_PARENT_SESSION_ID",
18
- "PI_SUBAGENT_PARENT_SESSION",
19
- ] as const;
20
-
21
- export interface ForwardedPermissionRequest {
22
- id: string;
23
- createdAt: number;
24
- requesterSessionId: string;
25
- targetSessionId: string;
26
- message: string;
27
- surface: string;
28
- value: string;
29
- }
30
-
31
- export interface ForwardedPermissionResponse {
32
- approved: boolean;
33
- state: PermissionPromptDecision["state"];
34
- denialReason?: string;
35
- responderSessionId: string;
36
- respondedAt: number;
37
- }
38
-
39
- interface EventBus {
40
- emit(channel: string, payload: unknown): void;
41
- on(channel: string, handler: (payload: unknown) => void): () => void;
42
- }
43
-
44
- export function resolveParentSessionId(): string | null {
45
- for (const key of SUBAGENT_PARENT_SESSION_ENV_CANDIDATES) {
46
- const value = process.env[key]?.trim();
47
- if (value) return value;
48
- }
49
- return null;
50
- }
51
-
52
- function getForwardingDirs(sessionRootDir: string): {
53
- requestsDir: string;
54
- responsesDir: string;
55
- } {
56
- const base = join(sessionRootDir, "permission-forwarding");
57
- const requestsDir = join(base, "requests");
58
- const responsesDir = join(base, "responses");
59
- mkdirSync(requestsDir, { recursive: true });
60
- mkdirSync(responsesDir, { recursive: true });
61
- return { requestsDir, responsesDir };
62
- }
63
-
64
- export async function forwardPermissionPrompt(options: {
65
- events?: EventBus;
66
- sessionRootDir?: string;
67
- requesterSessionId: string;
68
- targetSessionId: string;
69
- title: string;
70
- message: string;
71
- surface: string;
72
- value: string;
73
- timeoutMs?: number;
74
- }): Promise<PermissionPromptDecision | null> {
75
- const requestId = randomUUID();
76
- const payload = {
77
- requestId,
78
- targetSessionId: options.targetSessionId,
79
- requesterSessionId: options.requesterSessionId,
80
- title: options.title,
81
- message: options.message,
82
- surface: options.surface,
83
- value: options.value,
84
- };
85
-
86
- if (options.events) {
87
- return waitForRpcReply(
88
- options.events,
89
- payload,
90
- options.timeoutMs ?? 600_000,
91
- );
92
- }
93
-
94
- if (options.sessionRootDir) {
95
- return waitForFileReply(
96
- options.sessionRootDir,
97
- payload,
98
- options.timeoutMs ?? 600_000,
99
- );
100
- }
101
-
102
- return null;
103
- }
104
-
105
- function waitForRpcReply(
106
- events: EventBus,
107
- payload: {
108
- requestId: string;
109
- targetSessionId: string;
110
- requesterSessionId: string;
111
- title: string;
112
- message: string;
113
- surface: string;
114
- value: string;
115
- },
116
- timeoutMs: number,
117
- ): Promise<PermissionPromptDecision | null> {
118
- return new Promise((resolve) => {
119
- const replyChannel = `${PERMISSION_RPC_PROMPT_REPLY_PREFIX}${payload.requestId}`;
120
- let settled = false;
121
- const timer = setTimeout(() => {
122
- if (settled) return;
123
- settled = true;
124
- unsub();
125
- resolve(null);
126
- }, timeoutMs);
127
-
128
- const unsub = events.on(replyChannel, (reply) => {
129
- if (settled) return;
130
- settled = true;
131
- clearTimeout(timer);
132
- unsub();
133
- const decision = reply as PermissionPromptDecision;
134
- resolve(decision);
135
- });
136
-
137
- events.emit(PERMISSION_RPC_PROMPT, payload);
138
- });
139
- }
140
-
141
- function waitForFileReply(
142
- sessionRootDir: string,
143
- payload: {
144
- requestId: string;
145
- targetSessionId: string;
146
- requesterSessionId: string;
147
- title: string;
148
- message: string;
149
- surface: string;
150
- value: string;
151
- },
152
- timeoutMs: number,
153
- ): Promise<PermissionPromptDecision | null> {
154
- const { requestsDir, responsesDir } = getForwardingDirs(sessionRootDir);
155
- const request: ForwardedPermissionRequest = {
156
- id: payload.requestId,
157
- createdAt: Date.now(),
158
- requesterSessionId: payload.requesterSessionId,
159
- targetSessionId: payload.targetSessionId,
160
- message: payload.message,
161
- surface: payload.surface,
162
- value: payload.value,
163
- };
164
- writeFileSync(
165
- join(requestsDir, `${request.id}.json`),
166
- JSON.stringify(request),
167
- "utf8",
168
- );
169
-
170
- const started = Date.now();
171
- return new Promise((resolve) => {
172
- const timer = setInterval(() => {
173
- if (Date.now() - started > timeoutMs) {
174
- clearInterval(timer);
175
- resolve(null);
176
- return;
177
- }
178
- for (const file of readdirSync(responsesDir)) {
179
- if (!file.startsWith(request.id)) continue;
180
- try {
181
- const response = JSON.parse(
182
- readFileSync(join(responsesDir, file), "utf8"),
183
- ) as ForwardedPermissionResponse;
184
- unlinkSync(join(responsesDir, file));
185
- clearInterval(timer);
186
- resolve({
187
- approved: response.approved,
188
- state: response.state,
189
- denialReason: response.denialReason,
190
- });
191
- return;
192
- } catch {
193
- // keep polling
194
- }
195
- }
196
- }, 250);
197
- });
198
- }
199
-
200
- export function startPermissionRpcServer(
201
- events: EventBus,
202
- handler: (payload: {
203
- requestId: string;
204
- title: string;
205
- message: string;
206
- surface: string;
207
- value: string;
208
- }) => Promise<PermissionPromptDecision>,
209
- ): () => void {
210
- return events.on(PERMISSION_RPC_PROMPT, async (raw) => {
211
- const payload = raw as {
212
- requestId: string;
213
- title: string;
214
- message: string;
215
- surface: string;
216
- value: string;
217
- };
218
- const decision = await handler(payload);
219
- events.emit(
220
- `${PERMISSION_RPC_PROMPT_REPLY_PREFIX}${payload.requestId}`,
221
- decision,
222
- );
223
- });
224
- }
1
+ import { randomUUID } from "node:crypto";
2
+ import {
3
+ mkdirSync,
4
+ readdirSync,
5
+ readFileSync,
6
+ unlinkSync,
7
+ writeFileSync,
8
+ } from "node:fs";
9
+ import { join } from "node:path";
10
+ import type { PermissionPromptDecision } from "../ui/permission-dialog.ts";
11
+
12
+ export const PERMISSION_RPC_PROMPT = "permissions:rpc:prompt";
13
+ export const PERMISSION_RPC_PROMPT_REPLY_PREFIX =
14
+ "permissions:rpc:prompt:reply:";
15
+
16
+ export const SUBAGENT_PARENT_SESSION_ENV_CANDIDATES = [
17
+ "PI_AGENT_ROUTER_PARENT_SESSION_ID",
18
+ "PI_SUBAGENT_PARENT_SESSION",
19
+ ] as const;
20
+
21
+ export interface ForwardedPermissionRequest {
22
+ id: string;
23
+ createdAt: number;
24
+ requesterSessionId: string;
25
+ targetSessionId: string;
26
+ message: string;
27
+ surface: string;
28
+ value: string;
29
+ }
30
+
31
+ export interface ForwardedPermissionResponse {
32
+ approved: boolean;
33
+ state: PermissionPromptDecision["state"];
34
+ denialReason?: string;
35
+ responderSessionId: string;
36
+ respondedAt: number;
37
+ }
38
+
39
+ interface EventBus {
40
+ emit(channel: string, payload: unknown): void;
41
+ on(channel: string, handler: (payload: unknown) => void): () => void;
42
+ }
43
+
44
+ export function resolveParentSessionId(): string | null {
45
+ for (const key of SUBAGENT_PARENT_SESSION_ENV_CANDIDATES) {
46
+ const value = process.env[key]?.trim();
47
+ if (value) return value;
48
+ }
49
+ return null;
50
+ }
51
+
52
+ function getForwardingDirs(sessionRootDir: string): {
53
+ requestsDir: string;
54
+ responsesDir: string;
55
+ } {
56
+ const base = join(sessionRootDir, "permission-forwarding");
57
+ const requestsDir = join(base, "requests");
58
+ const responsesDir = join(base, "responses");
59
+ mkdirSync(requestsDir, { recursive: true });
60
+ mkdirSync(responsesDir, { recursive: true });
61
+ return { requestsDir, responsesDir };
62
+ }
63
+
64
+ export async function forwardPermissionPrompt(options: {
65
+ events?: EventBus;
66
+ sessionRootDir?: string;
67
+ requesterSessionId: string;
68
+ targetSessionId: string;
69
+ title: string;
70
+ message: string;
71
+ surface: string;
72
+ value: string;
73
+ timeoutMs?: number;
74
+ }): Promise<PermissionPromptDecision | null> {
75
+ const requestId = randomUUID();
76
+ const payload = {
77
+ requestId,
78
+ targetSessionId: options.targetSessionId,
79
+ requesterSessionId: options.requesterSessionId,
80
+ title: options.title,
81
+ message: options.message,
82
+ surface: options.surface,
83
+ value: options.value,
84
+ };
85
+
86
+ if (options.events) {
87
+ return waitForRpcReply(
88
+ options.events,
89
+ payload,
90
+ options.timeoutMs ?? 600_000,
91
+ );
92
+ }
93
+
94
+ if (options.sessionRootDir) {
95
+ return waitForFileReply(
96
+ options.sessionRootDir,
97
+ payload,
98
+ options.timeoutMs ?? 600_000,
99
+ );
100
+ }
101
+
102
+ return null;
103
+ }
104
+
105
+ function waitForRpcReply(
106
+ events: EventBus,
107
+ payload: {
108
+ requestId: string;
109
+ targetSessionId: string;
110
+ requesterSessionId: string;
111
+ title: string;
112
+ message: string;
113
+ surface: string;
114
+ value: string;
115
+ },
116
+ timeoutMs: number,
117
+ ): Promise<PermissionPromptDecision | null> {
118
+ return new Promise((resolve) => {
119
+ const replyChannel = `${PERMISSION_RPC_PROMPT_REPLY_PREFIX}${payload.requestId}`;
120
+ let settled = false;
121
+ const timer = setTimeout(() => {
122
+ if (settled) return;
123
+ settled = true;
124
+ unsub();
125
+ resolve(null);
126
+ }, timeoutMs);
127
+
128
+ const unsub = events.on(replyChannel, (reply) => {
129
+ if (settled) return;
130
+ settled = true;
131
+ clearTimeout(timer);
132
+ unsub();
133
+ const decision = reply as PermissionPromptDecision;
134
+ resolve(decision);
135
+ });
136
+
137
+ events.emit(PERMISSION_RPC_PROMPT, payload);
138
+ });
139
+ }
140
+
141
+ function waitForFileReply(
142
+ sessionRootDir: string,
143
+ payload: {
144
+ requestId: string;
145
+ targetSessionId: string;
146
+ requesterSessionId: string;
147
+ title: string;
148
+ message: string;
149
+ surface: string;
150
+ value: string;
151
+ },
152
+ timeoutMs: number,
153
+ ): Promise<PermissionPromptDecision | null> {
154
+ const { requestsDir, responsesDir } = getForwardingDirs(sessionRootDir);
155
+ const request: ForwardedPermissionRequest = {
156
+ id: payload.requestId,
157
+ createdAt: Date.now(),
158
+ requesterSessionId: payload.requesterSessionId,
159
+ targetSessionId: payload.targetSessionId,
160
+ message: payload.message,
161
+ surface: payload.surface,
162
+ value: payload.value,
163
+ };
164
+ writeFileSync(
165
+ join(requestsDir, `${request.id}.json`),
166
+ JSON.stringify(request),
167
+ "utf8",
168
+ );
169
+
170
+ const started = Date.now();
171
+ return new Promise((resolve) => {
172
+ const timer = setInterval(() => {
173
+ if (Date.now() - started > timeoutMs) {
174
+ clearInterval(timer);
175
+ resolve(null);
176
+ return;
177
+ }
178
+ for (const file of readdirSync(responsesDir)) {
179
+ if (!file.startsWith(request.id)) continue;
180
+ try {
181
+ const response = JSON.parse(
182
+ readFileSync(join(responsesDir, file), "utf8"),
183
+ ) as ForwardedPermissionResponse;
184
+ unlinkSync(join(responsesDir, file));
185
+ clearInterval(timer);
186
+ resolve({
187
+ approved: response.approved,
188
+ state: response.state,
189
+ denialReason: response.denialReason,
190
+ });
191
+ return;
192
+ } catch {
193
+ // keep polling
194
+ }
195
+ }
196
+ }, 250);
197
+ });
198
+ }
199
+
200
+ export function startPermissionRpcServer(
201
+ events: EventBus,
202
+ handler: (payload: {
203
+ requestId: string;
204
+ title: string;
205
+ message: string;
206
+ surface: string;
207
+ value: string;
208
+ }) => Promise<PermissionPromptDecision>,
209
+ ): () => void {
210
+ return events.on(PERMISSION_RPC_PROMPT, async (raw) => {
211
+ const payload = raw as {
212
+ requestId: string;
213
+ title: string;
214
+ message: string;
215
+ surface: string;
216
+ value: string;
217
+ };
218
+ const decision = await handler(payload);
219
+ events.emit(
220
+ `${PERMISSION_RPC_PROMPT_REPLY_PREFIX}${payload.requestId}`,
221
+ decision,
222
+ );
223
+ });
224
+ }