cuelo 0.10.82 → 0.10.84

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 (357) hide show
  1. package/.next/BUILD_ID +1 -1
  2. package/.next/app-path-routes-manifest.json +25 -23
  3. package/.next/build-manifest.json +2 -2
  4. package/.next/prerender-manifest.json +3 -3
  5. package/.next/required-server-files.js +1 -1
  6. package/.next/required-server-files.json +1 -1
  7. package/.next/routes-manifest.json +14 -0
  8. package/.next/server/app/_global-error/page.js +3 -3
  9. package/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  10. package/.next/server/app/_global-error.html +1 -1
  11. package/.next/server/app/_global-error.rsc +1 -1
  12. package/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  13. package/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  14. package/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  15. package/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  16. package/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  17. package/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  18. package/.next/server/app/_not-found/page.js +2 -2
  19. package/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  20. package/.next/server/app/_not-found.html +1 -1
  21. package/.next/server/app/_not-found.rsc +3 -3
  22. package/.next/server/app/_not-found.segments/_full.segment.rsc +3 -3
  23. package/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  24. package/.next/server/app/_not-found.segments/_index.segment.rsc +3 -3
  25. package/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  26. package/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  27. package/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
  28. package/.next/server/app/api/agent/[id]/account/route.js +1 -1
  29. package/.next/server/app/api/agent/[id]/bash-output/route.js +1 -1
  30. package/.next/server/app/api/agent/[id]/events/route.js +2 -2
  31. package/.next/server/app/api/agent/[id]/route.js +1 -1
  32. package/.next/server/app/api/agent/new/route.js +1 -1
  33. package/.next/server/app/api/agent/running/route.js +1 -1
  34. package/.next/server/app/api/attachment-settings/route.js +1 -1
  35. package/.next/server/app/api/attachments/[id]/transcription/route.js +2 -2
  36. package/.next/server/app/api/attachments/draft/route.js +1 -1
  37. package/.next/server/app/api/attachments/route.js +2 -2
  38. package/.next/server/app/api/auth/all-providers/route.js +1 -1
  39. package/.next/server/app/api/auth/api-key/[provider]/route.js +1 -1
  40. package/.next/server/app/api/auth/login/[provider]/route.js +2 -2
  41. package/.next/server/app/api/auth/logout/[provider]/route.js +1 -1
  42. package/.next/server/app/api/auth/providers/route.js +1 -1
  43. package/.next/server/app/api/character-mode/route.js +2 -0
  44. package/.next/server/app/api/character-mode/route.js.nft.json +1 -0
  45. package/.next/server/app/api/character-mode/route_client-reference-manifest.js +1 -0
  46. package/.next/server/app/api/cwd/browse/route.js +1 -1
  47. package/.next/server/app/api/cwd/validate/route.js +1 -1
  48. package/.next/server/app/api/default-cwd/route.js +1 -1
  49. package/.next/server/app/api/display-settings/route.js +1 -1
  50. package/.next/server/app/api/document-text/route.js +2 -2
  51. package/.next/server/app/api/experiments/route.js +1 -1
  52. package/.next/server/app/api/file-index/route.js +1 -1
  53. package/.next/server/app/api/files/[...path]/route.js +1 -1
  54. package/.next/server/app/api/git/diff/route.js +2 -2
  55. package/.next/server/app/api/git/status/route.js +2 -2
  56. package/.next/server/app/api/github/projects/route.js +1 -1
  57. package/.next/server/app/api/gpt6/handles/route.js +1 -1
  58. package/.next/server/app/api/gpt6/reply/route.js +1 -1
  59. package/.next/server/app/api/gpt6/token/route.js +1 -1
  60. package/.next/server/app/api/gpt6/web6-session/route.js +1 -1
  61. package/.next/server/app/api/home/route.js +1 -1
  62. package/.next/server/app/api/live/call/[callId]/route.js +1 -1
  63. package/.next/server/app/api/live/call/[callId]/route.js.nft.json +1 -1
  64. package/.next/server/app/api/live/events/route.js +2 -2
  65. package/.next/server/app/api/live/events/route.js.nft.json +1 -1
  66. package/.next/server/app/api/live/interpret/cloud/events/route.js +2 -2
  67. package/.next/server/app/api/live/interpret/cloud/events/route.js.nft.json +1 -1
  68. package/.next/server/app/api/live/interpret/cloud/input/route.js +1 -1
  69. package/.next/server/app/api/live/interpret/cloud/input/route.js.nft.json +1 -1
  70. package/.next/server/app/api/live/interpret/cloud/route.js +1 -1
  71. package/.next/server/app/api/live/interpret/cloud/route.js.nft.json +1 -1
  72. package/.next/server/app/api/live/interpret/route.js +1 -1
  73. package/.next/server/app/api/live/interpret/route.js.nft.json +1 -1
  74. package/.next/server/app/api/live/offer/route.js +1 -1
  75. package/.next/server/app/api/live/offer/route.js.nft.json +1 -1
  76. package/.next/server/app/api/live/voices/route.js +1 -1
  77. package/.next/server/app/api/live/voices/route.js.nft.json +1 -1
  78. package/.next/server/app/api/mcp/route.js +2 -2
  79. package/.next/server/app/api/model-roles/route.js +1 -1
  80. package/.next/server/app/api/models/route.js +1 -1
  81. package/.next/server/app/api/models-config/catalog/route.js +1 -1
  82. package/.next/server/app/api/models-config/discover/route.js +1 -1
  83. package/.next/server/app/api/models-config/route.js +2 -2
  84. package/.next/server/app/api/models-config/test/route.js +1 -1
  85. package/.next/server/app/api/office-characters/[...path]/route.js +2 -0
  86. package/.next/server/app/api/office-characters/[...path]/route.js.nft.json +1 -0
  87. package/.next/server/app/api/office-characters/[...path]/route_client-reference-manifest.js +1 -0
  88. package/.next/server/app/api/plugins/route.js +1 -1
  89. package/.next/server/app/api/project-docs/route.js +1 -1
  90. package/.next/server/app/api/project-trust/route.js +1 -1
  91. package/.next/server/app/api/projects/route.js +1 -1
  92. package/.next/server/app/api/runs/[runId]/prelude/route.js +1 -1
  93. package/.next/server/app/api/runs/[runId]/route.js +1 -1
  94. package/.next/server/app/api/runs/[runId]/turns/route.js +1 -1
  95. package/.next/server/app/api/runs/route.js +1 -1
  96. package/.next/server/app/api/sessions/[id]/auto-name/route.js +1 -1
  97. package/.next/server/app/api/sessions/[id]/context/route.js +1 -1
  98. package/.next/server/app/api/sessions/[id]/entries/[entryId]/thinking/route.js +1 -1
  99. package/.next/server/app/api/sessions/[id]/export/route.js +1 -1
  100. package/.next/server/app/api/sessions/[id]/route.js +1 -1
  101. package/.next/server/app/api/sessions/[id]/state/route.js +1 -1
  102. package/.next/server/app/api/sessions/route.js +1 -1
  103. package/.next/server/app/api/sessions/search/route.js +1 -1
  104. package/.next/server/app/api/settings/route.js +1 -1
  105. package/.next/server/app/api/sidecars/[service]/[...path]/route.js +1 -1
  106. package/.next/server/app/api/skills/check/route.js +1 -1
  107. package/.next/server/app/api/skills/install/route.js +2 -2
  108. package/.next/server/app/api/skills/route.js +2 -2
  109. package/.next/server/app/api/skills/search/route.js +1 -1
  110. package/.next/server/app/api/skills/update/route.js +1 -1
  111. package/.next/server/app/api/subagents/eta/route.js +1 -1
  112. package/.next/server/app/api/theme/route.js +1 -1
  113. package/.next/server/app/api/tool-image/route.js +1 -1
  114. package/.next/server/app/api/update-maintenance/route.js +2 -2
  115. package/.next/server/app/api/updates/route.js +1 -1
  116. package/.next/server/app/api/web-access/recovery/route.js +1 -1
  117. package/.next/server/app/api/web-access/route.js +1 -1
  118. package/.next/server/app/api/web6/consults/route.js +1 -1
  119. package/.next/server/app/api/worktrees/route.js +1 -1
  120. package/.next/server/app/favicon.ico/route.js +1 -1
  121. package/.next/server/app/index.html +1 -1
  122. package/.next/server/app/index.rsc +5 -5
  123. package/.next/server/app/index.segments/__PAGE__.segment.rsc +3 -3
  124. package/.next/server/app/index.segments/_full.segment.rsc +5 -5
  125. package/.next/server/app/index.segments/_head.segment.rsc +1 -1
  126. package/.next/server/app/index.segments/_index.segment.rsc +3 -3
  127. package/.next/server/app/index.segments/_tree.segment.rsc +2 -2
  128. package/.next/server/app/manifest.webmanifest/route.js +2 -2
  129. package/.next/server/app/page.js +21 -20
  130. package/.next/server/app/page_client-reference-manifest.js +1 -1
  131. package/.next/server/app/recover/page.js +2 -2
  132. package/.next/server/app/recover/page_client-reference-manifest.js +1 -1
  133. package/.next/server/app/recover.html +1 -1
  134. package/.next/server/app/recover.rsc +3 -3
  135. package/.next/server/app/recover.segments/_full.segment.rsc +3 -3
  136. package/.next/server/app/recover.segments/_head.segment.rsc +1 -1
  137. package/.next/server/app/recover.segments/_index.segment.rsc +3 -3
  138. package/.next/server/app/recover.segments/_tree.segment.rsc +2 -2
  139. package/.next/server/app/recover.segments/recover/__PAGE__.segment.rsc +1 -1
  140. package/.next/server/app/recover.segments/recover.segment.rsc +1 -1
  141. package/.next/server/app-paths-manifest.json +25 -23
  142. package/.next/server/chunks/{6775.js → 205.js} +2 -2
  143. package/.next/server/chunks/3379.js +1 -1
  144. package/.next/server/chunks/7738.js +1 -0
  145. package/.next/server/functions-config-manifest.json +2 -0
  146. package/.next/server/middleware-build-manifest.js +1 -1
  147. package/.next/server/pages/404.html +1 -1
  148. package/.next/server/pages/500.html +1 -1
  149. package/.next/server/server-reference-manifest.json +1 -1
  150. package/.next/static/chunks/app/_global-error/page-436da0332762a8e3.js +1 -0
  151. package/.next/static/chunks/app/api/agent/[id]/account/route-436da0332762a8e3.js +1 -0
  152. package/.next/static/chunks/app/api/agent/[id]/bash-output/route-436da0332762a8e3.js +1 -0
  153. package/.next/static/chunks/app/api/agent/[id]/events/route-436da0332762a8e3.js +1 -0
  154. package/.next/static/chunks/app/api/agent/[id]/route-436da0332762a8e3.js +1 -0
  155. package/.next/static/chunks/app/api/agent/new/route-436da0332762a8e3.js +1 -0
  156. package/.next/static/chunks/app/api/agent/running/route-436da0332762a8e3.js +1 -0
  157. package/.next/static/chunks/app/api/attachment-settings/route-436da0332762a8e3.js +1 -0
  158. package/.next/static/chunks/app/api/attachments/[id]/transcription/route-436da0332762a8e3.js +1 -0
  159. package/.next/static/chunks/app/api/attachments/draft/route-436da0332762a8e3.js +1 -0
  160. package/.next/static/chunks/app/api/attachments/route-436da0332762a8e3.js +1 -0
  161. package/.next/static/chunks/app/api/auth/all-providers/route-436da0332762a8e3.js +1 -0
  162. package/.next/static/chunks/app/api/auth/api-key/[provider]/route-436da0332762a8e3.js +1 -0
  163. package/.next/static/chunks/app/api/auth/login/[provider]/route-436da0332762a8e3.js +1 -0
  164. package/.next/static/chunks/app/api/auth/logout/[provider]/route-436da0332762a8e3.js +1 -0
  165. package/.next/static/chunks/app/api/auth/providers/route-436da0332762a8e3.js +1 -0
  166. package/.next/static/chunks/app/api/character-mode/route-436da0332762a8e3.js +1 -0
  167. package/.next/static/chunks/app/api/cwd/browse/route-436da0332762a8e3.js +1 -0
  168. package/.next/static/chunks/app/api/cwd/validate/route-436da0332762a8e3.js +1 -0
  169. package/.next/static/chunks/app/api/default-cwd/route-436da0332762a8e3.js +1 -0
  170. package/.next/static/chunks/app/api/display-settings/route-436da0332762a8e3.js +1 -0
  171. package/.next/static/chunks/app/api/document-text/route-436da0332762a8e3.js +1 -0
  172. package/.next/static/chunks/app/api/experiments/route-436da0332762a8e3.js +1 -0
  173. package/.next/static/chunks/app/api/file-index/route-436da0332762a8e3.js +1 -0
  174. package/.next/static/chunks/app/api/files/[...path]/route-436da0332762a8e3.js +1 -0
  175. package/.next/static/chunks/app/api/git/diff/route-436da0332762a8e3.js +1 -0
  176. package/.next/static/chunks/app/api/git/status/route-436da0332762a8e3.js +1 -0
  177. package/.next/static/chunks/app/api/github/projects/route-436da0332762a8e3.js +1 -0
  178. package/.next/static/chunks/app/api/gpt6/handles/route-436da0332762a8e3.js +1 -0
  179. package/.next/static/chunks/app/api/gpt6/reply/route-436da0332762a8e3.js +1 -0
  180. package/.next/static/chunks/app/api/gpt6/token/route-436da0332762a8e3.js +1 -0
  181. package/.next/static/chunks/app/api/gpt6/web6-session/route-436da0332762a8e3.js +1 -0
  182. package/.next/static/chunks/app/api/home/route-436da0332762a8e3.js +1 -0
  183. package/.next/static/chunks/app/api/live/call/[callId]/route-436da0332762a8e3.js +1 -0
  184. package/.next/static/chunks/app/api/live/events/route-436da0332762a8e3.js +1 -0
  185. package/.next/static/chunks/app/api/live/interpret/cloud/events/route-436da0332762a8e3.js +1 -0
  186. package/.next/static/chunks/app/api/live/interpret/cloud/input/route-436da0332762a8e3.js +1 -0
  187. package/.next/static/chunks/app/api/live/interpret/cloud/route-436da0332762a8e3.js +1 -0
  188. package/.next/static/chunks/app/api/live/interpret/route-436da0332762a8e3.js +1 -0
  189. package/.next/static/chunks/app/api/live/offer/route-436da0332762a8e3.js +1 -0
  190. package/.next/static/chunks/app/api/live/voices/route-436da0332762a8e3.js +1 -0
  191. package/.next/static/chunks/app/api/mcp/route-436da0332762a8e3.js +1 -0
  192. package/.next/static/chunks/app/api/model-roles/route-436da0332762a8e3.js +1 -0
  193. package/.next/static/chunks/app/api/models/route-436da0332762a8e3.js +1 -0
  194. package/.next/static/chunks/app/api/models-config/catalog/route-436da0332762a8e3.js +1 -0
  195. package/.next/static/chunks/app/api/models-config/discover/route-436da0332762a8e3.js +1 -0
  196. package/.next/static/chunks/app/api/models-config/route-436da0332762a8e3.js +1 -0
  197. package/.next/static/chunks/app/api/models-config/test/route-436da0332762a8e3.js +1 -0
  198. package/.next/static/chunks/app/api/office-characters/[...path]/route-436da0332762a8e3.js +1 -0
  199. package/.next/static/chunks/app/api/plugins/route-436da0332762a8e3.js +1 -0
  200. package/.next/static/chunks/app/api/project-docs/route-436da0332762a8e3.js +1 -0
  201. package/.next/static/chunks/app/api/project-trust/route-436da0332762a8e3.js +1 -0
  202. package/.next/static/chunks/app/api/projects/route-436da0332762a8e3.js +1 -0
  203. package/.next/static/chunks/app/api/runs/[runId]/prelude/route-436da0332762a8e3.js +1 -0
  204. package/.next/static/chunks/app/api/runs/[runId]/route-436da0332762a8e3.js +1 -0
  205. package/.next/static/chunks/app/api/runs/[runId]/turns/route-436da0332762a8e3.js +1 -0
  206. package/.next/static/chunks/app/api/runs/route-436da0332762a8e3.js +1 -0
  207. package/.next/static/chunks/app/api/sessions/[id]/auto-name/route-436da0332762a8e3.js +1 -0
  208. package/.next/static/chunks/app/api/sessions/[id]/context/route-436da0332762a8e3.js +1 -0
  209. package/.next/static/chunks/app/api/sessions/[id]/entries/[entryId]/thinking/route-436da0332762a8e3.js +1 -0
  210. package/.next/static/chunks/app/api/sessions/[id]/export/route-436da0332762a8e3.js +1 -0
  211. package/.next/static/chunks/app/api/sessions/[id]/route-436da0332762a8e3.js +1 -0
  212. package/.next/static/chunks/app/api/sessions/[id]/state/route-436da0332762a8e3.js +1 -0
  213. package/.next/static/chunks/app/api/sessions/route-436da0332762a8e3.js +1 -0
  214. package/.next/static/chunks/app/api/sessions/search/route-436da0332762a8e3.js +1 -0
  215. package/.next/static/chunks/app/api/settings/route-436da0332762a8e3.js +1 -0
  216. package/.next/static/chunks/app/api/sidecars/[service]/[...path]/route-436da0332762a8e3.js +1 -0
  217. package/.next/static/chunks/app/api/skills/check/route-436da0332762a8e3.js +1 -0
  218. package/.next/static/chunks/app/api/skills/install/route-436da0332762a8e3.js +1 -0
  219. package/.next/static/chunks/app/api/skills/route-436da0332762a8e3.js +1 -0
  220. package/.next/static/chunks/app/api/skills/search/route-436da0332762a8e3.js +1 -0
  221. package/.next/static/chunks/app/api/skills/update/route-436da0332762a8e3.js +1 -0
  222. package/.next/static/chunks/app/api/subagents/eta/route-436da0332762a8e3.js +1 -0
  223. package/.next/static/chunks/app/api/theme/route-436da0332762a8e3.js +1 -0
  224. package/.next/static/chunks/app/api/tool-image/route-436da0332762a8e3.js +1 -0
  225. package/.next/static/chunks/app/api/update-maintenance/route-436da0332762a8e3.js +1 -0
  226. package/.next/static/chunks/app/api/updates/route-436da0332762a8e3.js +1 -0
  227. package/.next/static/chunks/app/api/web-access/recovery/route-436da0332762a8e3.js +1 -0
  228. package/.next/static/chunks/app/api/web-access/route-436da0332762a8e3.js +1 -0
  229. package/.next/static/chunks/app/api/web6/consults/route-436da0332762a8e3.js +1 -0
  230. package/.next/static/chunks/app/api/worktrees/route-436da0332762a8e3.js +1 -0
  231. package/.next/static/chunks/app/{layout-40f42db345121a86.js → layout-ce91947307be9e42.js} +1 -1
  232. package/.next/static/chunks/app/manifest.webmanifest/route-436da0332762a8e3.js +1 -0
  233. package/.next/static/chunks/app/page-68ca3d1bdecd1271.js +75 -0
  234. package/.next/static/chunks/next/dist/client/components/builtin/app-error-436da0332762a8e3.js +1 -0
  235. package/.next/static/chunks/next/dist/client/components/builtin/forbidden-436da0332762a8e3.js +1 -0
  236. package/.next/static/chunks/next/dist/client/components/builtin/not-found-436da0332762a8e3.js +1 -0
  237. package/.next/static/chunks/next/dist/client/components/builtin/unauthorized-436da0332762a8e3.js +1 -0
  238. package/.next/static/css/4b1cc49bf76b21fd.css +1 -0
  239. package/.next/static/lo0tWf3C3oP8rBCPntG26/_buildManifest.js +1 -0
  240. package/.next/trace +108 -108
  241. package/.next/trace-build +1 -1
  242. package/.next/types/app/api/character-mode/route.ts +351 -0
  243. package/.next/types/app/api/office-characters/[...path]/route.ts +351 -0
  244. package/.next/types/routes.d.ts +3 -1
  245. package/.next/types/validator.ts +18 -0
  246. package/README.md +1 -0
  247. package/Tools/CUELO_Setup/files/native-runtime-patch.js +1 -1
  248. package/Tools/CUELO_Setup/files/usage-server.js +745 -3
  249. package/Tools/OMP_Global_Config/agent/RULES.md +20 -1
  250. package/Tools/OMP_Global_Config/agent/agents/maker.md +6 -2
  251. package/Tools/OMP_Global_Config/agent/extensions/character-voice.ts +217 -22
  252. package/Tools/OMP_Global_Config/agent/extensions/cross-review.ts +7 -4
  253. package/Tools/OMP_Global_Config/agent/extensions/draft/pdf.ts +205 -25
  254. package/Tools/OMP_Global_Config/agent/extensions/draft.ts +8 -5
  255. package/Tools/OMP_Global_Config/agent/extensions/lib/cross-review.ts +1 -1
  256. package/Tools/OMP_Global_Config/agent/extensions/lib/slop-gate.ts +397 -0
  257. package/Tools/OMP_Global_Config/agent/extensions/slop-gate/delta.ts +83 -0
  258. package/Tools/OMP_Global_Config/agent/extensions/slop-gate/extract.py +86 -0
  259. package/Tools/OMP_Global_Config/agent/extensions/slop-gate/extract.ts +128 -0
  260. package/Tools/OMP_Global_Config/agent/extensions/slop-gate/review.ts +408 -0
  261. package/Tools/OMP_Global_Config/agent/extensions/slop-gate.ts +125 -0
  262. package/Tools/OMP_Global_Config/agent/prompt-copies/RULES.en.md +5 -5
  263. package/Tools/OMP_Global_Config/agent/rules/harness-policy.json +21 -4
  264. package/Tools/OMP_Global_Config/agent/rules/meta-harness.md +2 -2
  265. package/Tools/OMP_Global_Config/agent/rules/subagent.md +8 -8
  266. package/Tools/OMP_Global_Config/agent/sop/maker.md +5 -1
  267. package/Tools/OMP_Global_Config/agent/tools/ego-browser/index.ts +1 -1
  268. package/Tools/OMP_Global_Config/agent/tools/git-finalizer/index.ts +14 -1
  269. package/package.json +1 -1
  270. package/.next/static/1ZhWi9jNg8U0FotDp3xvj/_buildManifest.js +0 -1
  271. package/.next/static/chunks/app/_global-error/page-60d4ba1619c3b27a.js +0 -1
  272. package/.next/static/chunks/app/api/agent/[id]/account/route-60d4ba1619c3b27a.js +0 -1
  273. package/.next/static/chunks/app/api/agent/[id]/bash-output/route-60d4ba1619c3b27a.js +0 -1
  274. package/.next/static/chunks/app/api/agent/[id]/events/route-60d4ba1619c3b27a.js +0 -1
  275. package/.next/static/chunks/app/api/agent/[id]/route-60d4ba1619c3b27a.js +0 -1
  276. package/.next/static/chunks/app/api/agent/new/route-60d4ba1619c3b27a.js +0 -1
  277. package/.next/static/chunks/app/api/agent/running/route-60d4ba1619c3b27a.js +0 -1
  278. package/.next/static/chunks/app/api/attachment-settings/route-60d4ba1619c3b27a.js +0 -1
  279. package/.next/static/chunks/app/api/attachments/[id]/transcription/route-60d4ba1619c3b27a.js +0 -1
  280. package/.next/static/chunks/app/api/attachments/draft/route-60d4ba1619c3b27a.js +0 -1
  281. package/.next/static/chunks/app/api/attachments/route-60d4ba1619c3b27a.js +0 -1
  282. package/.next/static/chunks/app/api/auth/all-providers/route-60d4ba1619c3b27a.js +0 -1
  283. package/.next/static/chunks/app/api/auth/api-key/[provider]/route-60d4ba1619c3b27a.js +0 -1
  284. package/.next/static/chunks/app/api/auth/login/[provider]/route-60d4ba1619c3b27a.js +0 -1
  285. package/.next/static/chunks/app/api/auth/logout/[provider]/route-60d4ba1619c3b27a.js +0 -1
  286. package/.next/static/chunks/app/api/auth/providers/route-60d4ba1619c3b27a.js +0 -1
  287. package/.next/static/chunks/app/api/cwd/browse/route-60d4ba1619c3b27a.js +0 -1
  288. package/.next/static/chunks/app/api/cwd/validate/route-60d4ba1619c3b27a.js +0 -1
  289. package/.next/static/chunks/app/api/default-cwd/route-60d4ba1619c3b27a.js +0 -1
  290. package/.next/static/chunks/app/api/display-settings/route-60d4ba1619c3b27a.js +0 -1
  291. package/.next/static/chunks/app/api/document-text/route-60d4ba1619c3b27a.js +0 -1
  292. package/.next/static/chunks/app/api/experiments/route-60d4ba1619c3b27a.js +0 -1
  293. package/.next/static/chunks/app/api/file-index/route-60d4ba1619c3b27a.js +0 -1
  294. package/.next/static/chunks/app/api/files/[...path]/route-60d4ba1619c3b27a.js +0 -1
  295. package/.next/static/chunks/app/api/git/diff/route-60d4ba1619c3b27a.js +0 -1
  296. package/.next/static/chunks/app/api/git/status/route-60d4ba1619c3b27a.js +0 -1
  297. package/.next/static/chunks/app/api/github/projects/route-60d4ba1619c3b27a.js +0 -1
  298. package/.next/static/chunks/app/api/gpt6/handles/route-60d4ba1619c3b27a.js +0 -1
  299. package/.next/static/chunks/app/api/gpt6/reply/route-60d4ba1619c3b27a.js +0 -1
  300. package/.next/static/chunks/app/api/gpt6/token/route-60d4ba1619c3b27a.js +0 -1
  301. package/.next/static/chunks/app/api/gpt6/web6-session/route-60d4ba1619c3b27a.js +0 -1
  302. package/.next/static/chunks/app/api/home/route-60d4ba1619c3b27a.js +0 -1
  303. package/.next/static/chunks/app/api/live/call/[callId]/route-60d4ba1619c3b27a.js +0 -1
  304. package/.next/static/chunks/app/api/live/events/route-60d4ba1619c3b27a.js +0 -1
  305. package/.next/static/chunks/app/api/live/interpret/cloud/events/route-60d4ba1619c3b27a.js +0 -1
  306. package/.next/static/chunks/app/api/live/interpret/cloud/input/route-60d4ba1619c3b27a.js +0 -1
  307. package/.next/static/chunks/app/api/live/interpret/cloud/route-60d4ba1619c3b27a.js +0 -1
  308. package/.next/static/chunks/app/api/live/interpret/route-60d4ba1619c3b27a.js +0 -1
  309. package/.next/static/chunks/app/api/live/offer/route-60d4ba1619c3b27a.js +0 -1
  310. package/.next/static/chunks/app/api/live/voices/route-60d4ba1619c3b27a.js +0 -1
  311. package/.next/static/chunks/app/api/mcp/route-60d4ba1619c3b27a.js +0 -1
  312. package/.next/static/chunks/app/api/model-roles/route-60d4ba1619c3b27a.js +0 -1
  313. package/.next/static/chunks/app/api/models/route-60d4ba1619c3b27a.js +0 -1
  314. package/.next/static/chunks/app/api/models-config/catalog/route-60d4ba1619c3b27a.js +0 -1
  315. package/.next/static/chunks/app/api/models-config/discover/route-60d4ba1619c3b27a.js +0 -1
  316. package/.next/static/chunks/app/api/models-config/route-60d4ba1619c3b27a.js +0 -1
  317. package/.next/static/chunks/app/api/models-config/test/route-60d4ba1619c3b27a.js +0 -1
  318. package/.next/static/chunks/app/api/plugins/route-60d4ba1619c3b27a.js +0 -1
  319. package/.next/static/chunks/app/api/project-docs/route-60d4ba1619c3b27a.js +0 -1
  320. package/.next/static/chunks/app/api/project-trust/route-60d4ba1619c3b27a.js +0 -1
  321. package/.next/static/chunks/app/api/projects/route-60d4ba1619c3b27a.js +0 -1
  322. package/.next/static/chunks/app/api/runs/[runId]/prelude/route-60d4ba1619c3b27a.js +0 -1
  323. package/.next/static/chunks/app/api/runs/[runId]/route-60d4ba1619c3b27a.js +0 -1
  324. package/.next/static/chunks/app/api/runs/[runId]/turns/route-60d4ba1619c3b27a.js +0 -1
  325. package/.next/static/chunks/app/api/runs/route-60d4ba1619c3b27a.js +0 -1
  326. package/.next/static/chunks/app/api/sessions/[id]/auto-name/route-60d4ba1619c3b27a.js +0 -1
  327. package/.next/static/chunks/app/api/sessions/[id]/context/route-60d4ba1619c3b27a.js +0 -1
  328. package/.next/static/chunks/app/api/sessions/[id]/entries/[entryId]/thinking/route-60d4ba1619c3b27a.js +0 -1
  329. package/.next/static/chunks/app/api/sessions/[id]/export/route-60d4ba1619c3b27a.js +0 -1
  330. package/.next/static/chunks/app/api/sessions/[id]/route-60d4ba1619c3b27a.js +0 -1
  331. package/.next/static/chunks/app/api/sessions/[id]/state/route-60d4ba1619c3b27a.js +0 -1
  332. package/.next/static/chunks/app/api/sessions/route-60d4ba1619c3b27a.js +0 -1
  333. package/.next/static/chunks/app/api/sessions/search/route-60d4ba1619c3b27a.js +0 -1
  334. package/.next/static/chunks/app/api/settings/route-60d4ba1619c3b27a.js +0 -1
  335. package/.next/static/chunks/app/api/sidecars/[service]/[...path]/route-60d4ba1619c3b27a.js +0 -1
  336. package/.next/static/chunks/app/api/skills/check/route-60d4ba1619c3b27a.js +0 -1
  337. package/.next/static/chunks/app/api/skills/install/route-60d4ba1619c3b27a.js +0 -1
  338. package/.next/static/chunks/app/api/skills/route-60d4ba1619c3b27a.js +0 -1
  339. package/.next/static/chunks/app/api/skills/search/route-60d4ba1619c3b27a.js +0 -1
  340. package/.next/static/chunks/app/api/skills/update/route-60d4ba1619c3b27a.js +0 -1
  341. package/.next/static/chunks/app/api/subagents/eta/route-60d4ba1619c3b27a.js +0 -1
  342. package/.next/static/chunks/app/api/theme/route-60d4ba1619c3b27a.js +0 -1
  343. package/.next/static/chunks/app/api/tool-image/route-60d4ba1619c3b27a.js +0 -1
  344. package/.next/static/chunks/app/api/update-maintenance/route-60d4ba1619c3b27a.js +0 -1
  345. package/.next/static/chunks/app/api/updates/route-60d4ba1619c3b27a.js +0 -1
  346. package/.next/static/chunks/app/api/web-access/recovery/route-60d4ba1619c3b27a.js +0 -1
  347. package/.next/static/chunks/app/api/web-access/route-60d4ba1619c3b27a.js +0 -1
  348. package/.next/static/chunks/app/api/web6/consults/route-60d4ba1619c3b27a.js +0 -1
  349. package/.next/static/chunks/app/api/worktrees/route-60d4ba1619c3b27a.js +0 -1
  350. package/.next/static/chunks/app/manifest.webmanifest/route-60d4ba1619c3b27a.js +0 -1
  351. package/.next/static/chunks/app/page-6af6456e63f1cf12.js +0 -74
  352. package/.next/static/chunks/next/dist/client/components/builtin/app-error-60d4ba1619c3b27a.js +0 -1
  353. package/.next/static/chunks/next/dist/client/components/builtin/forbidden-60d4ba1619c3b27a.js +0 -1
  354. package/.next/static/chunks/next/dist/client/components/builtin/not-found-60d4ba1619c3b27a.js +0 -1
  355. package/.next/static/chunks/next/dist/client/components/builtin/unauthorized-60d4ba1619c3b27a.js +0 -1
  356. package/.next/static/css/f55542101d0388e8.css +0 -1
  357. /package/.next/static/{1ZhWi9jNg8U0FotDp3xvj → lo0tWf3C3oP8rBCPntG26}/_ssgManifest.js +0 -0
@@ -92,6 +92,16 @@
92
92
  `edit`로 반영한다. 코드·설정·규칙 파일(`RULES.md`·`AGENTS.md`·`rules/`·SOP), 몇 줄 이하의 정확한 수정, 사용자에게
93
93
  하는 대화 답변은 대상이 아니다. 이 Gemini는 2026-10-26부터 Google Cloud Vertex의 Gemini 4 Argon이며(크레딧으로
94
94
  과금) OCR 역할에도 쓴다. 이미지 질문(vision)은 Opus를 유지하고, Antigravity OAuth는 계정 정지 위험으로 쓰지 않는다.
95
+ **밖에서 보이는 산출물은 AI 슬롭 게이트를 통과해야 한다(2026-10-26 사용자 결정).** 소스 코드 밖에서 사람이 읽는
96
+ 모든 것이 대상이다: README·CHANGELOG·`docs/`·공개 md, HTML·웹 화면, PPT, Word·Excel·PDF·HWPX·csv 보고서와 표,
97
+ 사용자에게 보이는 UI 문구(로캘 팩 등). 코드·주석·로그·테스트·생성물·내부 프롬프트와 규칙·스킬, 내부 기록
98
+ (`HANDOFF.md`·`doc/history`·`doc/README.md`)은 게이트 대상이 아니다(위 `draft`·점검 규칙은 그대로 적용된다).
99
+ 점검은 `slop_check` 도구로 하고, 검토 모델은 `cross_review`와 같은 교차검증 경로(쓴 모델의 반대 계열 + Argon 보조)다.
100
+ 지적이 없으면 그 파일 내용 그대로 통과가 기록된다. 고친 파일은 내용이 바뀌었으니 다시 점검하고, 내용을 바꾸지 않은 채
101
+ 닫을 수 있는 건 Main이 근거를 적어 반박(`refuted`)한 지적뿐이다. git이 추적하는 파일은 HEAD 대비 바뀐 글만 본다.
102
+ `git_finalize`는 통과 기록이 없는 대상 파일을 커밋하지 않고(단계 `slop-gate`), 저장소 밖에
103
+ 내보낸 결과물에 통과 기록이 없으면 턴을 끝내지 않고 이어서 점검하게 한다. 이 게이트는 사용자가 명시적으로 지시할
104
+ 때만 면제하며 면제는 기록된다.
95
105
  - **문서 기본 편집 도구는 LibreOffice다(2026-10-15 사용자 결정).** 기존 Word·Excel·PowerPoint 문서의
96
106
  열기·편집·변환은 Writer·Calc·Impress를 우선 사용한다. 새 문서 초안은 위 `draft` 규칙을 따른다.
97
107
  MS Office는 사용자가 지정했거나, 매크로·외부 연결·전용 기능·서식 호환성 때문에 필요한 경우에 쓴다.
@@ -105,7 +115,9 @@
105
115
  `browser.open`에 `app: { relay: false }`를 준 headless로 한다. 이를 빼면 relay가 기본이라 **사용자의 실제
106
116
  브라우저**에 탭이 열린다. relay 탭을 닫을 때는 `browser.close({ name })`로 자기 탭만
107
117
  닫고 `all`·`kill`·`app.path`는 쓰지 않는다 — 사용자 창까지 닫히거나 기존 프로세스에 붙는다.
108
- 빌드가 깨진 화면을 사용자에게 띄우지 않는다. 이미지·문서 확인은 `read`의 `경로?q=<질문>`으로
118
+ 빌드가 깨진 화면을 사용자에게 띄우지 않는다. HTML·웹 페이지를 PDF로 인쇄하는 것도 브라우저 작업이라 같은 경로를
119
+ 따른다: ego lite PC에서는 TaskSpace에서 `page.cdp("Page.printToPDF")`로 받고, Edge·Chrome `--print-to-pdf`를 따로
120
+ 띄우지 않는다(2026-10-26 사용자 지적). 이미지·문서 확인은 `read`의 `경로?q=<질문>`으로
109
121
  먼저 해결하고, 실제 DOM 렌더가 필요할 때만 브라우저를 연다. 글자·숫자·표 전사는 `경로?ocr=<질문>`(OCR 전용
110
122
  `ocr` 역할 모델)으로, 특정 문구·값 확인과 레이아웃·간격·색·정렬·잘림·좌표 판단은 `?q=`(`vision` 역할 모델)로
111
123
  먼저 묻는다. `?q=`·`?ocr=` 없는 이미지 read는 그 경로로 한 번 묻기 전까지 계속 막힌다. 그 답으로 판단할 수
@@ -117,6 +129,12 @@
117
129
  넘기고, 모양을 봐야 할 때만 스크린샷을 찍는다. `judge()` 결과는 참고 신호이며 최종 수용 근거가 아니다.
118
130
  `computer`는 사용자의 실제 데스크톱을 조작하므로 자기가 띄운 창만 다루고, 삭제·전송·결제처럼 되돌릴 수
119
131
  없는 버튼은 누르지 않는다.
132
+ **테스트·검증은 사용자 화면 밖에서 돌린다(2026-10-26 사용자 결정).** 웹 테스트는 사용자 탭과 떨어진 독립
133
+ 공간(ego lite PC는 검증 프로필 TaskSpace, 그 밖은 headless)에서만 한다. 앱·Windows 프로그램 테스트는 사용자가
134
+ 보는 데스크톱에 창을 띄우지 않고 백그라운드에서 돌린다: 숨은 Windows 데스크톱(별도 desktop 객체), 화면 밖
135
+ 위치나 표시 없는 하네스, 장비 없는 로직 테스트 중 대상에 맞는 것을 쓴다. 이 방식으로 확인할 수 없는 항목(실제
136
+ GPU·표시 장치가 필요한 렌더 등)은 미확인으로 남기고, 사용자 화면에 띄우는 건 사용자가 요청하거나 승인했을
137
+ 때만이다. 띄운 프로세스는 자기 PID로만 끝낸다.
120
138
  같은 순서를 **여러 문서·기록·로그를 분류할 때**와 **긴 작업을 감시할 때**도 쓴다. 헤더·정해진 문구로
121
139
  먼저 거르고, 남은 것이 넷 이상이면 `judge_batch`로 한 번에 나눈 뒤 애매한 것만 직접 읽는다. 배포·빌드·긴
122
140
  테스트는 셀 안 반복문에서 종료 문구를 문자열로 잡고, "멈췄나·진행 중인가"처럼 문구로 못 가르는 상태만
@@ -137,6 +155,7 @@
137
155
  맞출 때는 남은 행동·관찰 조건·새 경계만 남기고, 이번에 끝난 행은 지운다. 그 자리에서 끝난 작업은
138
156
  HANDOFF가 아니라 `doc/history`에 기록한다(`rule://docs-handoff`).
139
157
  - 코드 변경이 high-risk이거나 트리거 marker·결정적 10% 표본에 해당하면(문서·주석·테스트 제외) `git_finalize` 전에 `cross_review`로 다른 모델 계열(Anthropic 작성분은 OpenAI, OpenAI·그 밖 제공자 작성분은 Anthropic)의 검토를 받고 모든 finding을 근거와 함께 닫는다. 검토는 자문 증거이며 PASS가 아니다. 사용자의 명시 지시 없이 이 gate를 면제하지 않는다. 정본은 `harness-policy.json` `mainLane.workerReview.crossFamilyReview`다.
158
+ **검토 모델은 사용자가 정한 교차검증 경로만 쓴다(2026-10-26 사용자 지적).** 코드·문서·산출물의 검토, 검증, 게이트 판정에 쓰는 모델은 `config.yml` `modelRoles`의 `crossReviewOpenAI`·`crossReviewAnthropic`·`crossReviewGoogle` 슬롯에서 `cross_review`와 같은 계열 규칙으로 읽는다. 새 검토 기능이나 Maker 지시서에 다른 모델(예: Sonnet)을 비용·속도를 이유로 직접 지정하지 않고, 그 경로 밖의 모델로 본 결과를 교차검증·검토 통과라고 부르지 않는다. 경로를 바꾸려면 먼저 사용자에게 묻는다(2026-10-26: Main이 슬롭 게이트 검토 모델을 Sonnet으로 지시서에 넣었다가 사용자가 바로잡았다).
140
159
  - `git_finalize`는 상대경로 canonical target만 받고 한 호출에서 저장소 하나만 마감한다. 경로
141
160
  해석과 거부 대상, cwd 밖 별도 Git 저장소 허용, Main의 raw Git fallback 4조건과 사전 확인,
142
161
  remote·upstream·ancestry·mutex 계약의 정본은 `rule://subagent`의 「프로젝트와 Git 경계」다.
@@ -3,10 +3,10 @@ name: maker
3
3
  description: Delegated end-to-end implementation agent that investigates, edits, repairs, validates its own slice, and verifies the actual surface it changed.
4
4
  model: "@implSonnet"
5
5
  thinking-level: auto
6
- tools: [read, bash, edit, write, grep, glob, skim, draft, lsp, eval, generate_image, ast_grep, ast_edit, debug, todo, web_search, windows_build, windows_control, ego_browser, checkpoint, cross_review]
6
+ tools: [read, bash, edit, write, grep, glob, skim, draft, lsp, eval, generate_image, ast_grep, ast_edit, debug, todo, web_search, windows_build, windows_control, ego_browser, checkpoint, cross_review, slop_check]
7
7
  ---
8
8
 
9
- <!-- omp-global-config:generated source-hash=f62bd7c5ebc9
9
+ <!-- omp-global-config:generated source-hash=b829d26bbf84
10
10
  이 파일은 patches/build-agents.mjs 가 만든 빌드 산출물이다. 직접 수정하지 마라.
11
11
  고칠 곳: agent/sop/maker.md · agent/sop/_writer.md · agent/sop/_common.md
12
12
  재생성: node patches/build-agents.mjs 검사: node patches/build-agents.mjs --check
@@ -15,6 +15,10 @@ tools: [read, bash, edit, write, grep, glob, skim, draft, lsp, eval, generate_im
15
15
  You are the delegated implementation Maker on the default `modelRoles.implSonnet` slot. A delegated slice
16
16
  is yours end to end.
17
17
 
18
+ Visible artifacts you write (README/CHANGELOG/docs and other public markdown, html, pptx/docx/xlsx/pdf/hwpx, report csv, UI copy packs; never code, tests or internal records such as `doc/history`) must pass `slop_check` before you report: run it on the files with your own model selector as `authors`, fix every finding and run it again on the changed file (a pass is tied to the exact bytes). A false positive is reported to Main with evidence; `slop_check` resolve is Main-only. `git_finalize` refuses visible artifacts without that pass, and a turn that wrote one outside git does not end without it.
19
+
20
+ Code files you changed (the same scope `git_finalize` gates: no docs, comments or tests) get a `cross_review` `review` from you before you report, whether or not the brief asks for it: run it after your focused checks pass, with your own model selector as `authors` (never `"main"`), fix or refute each finding with evidence, use the probe round when a reviewer proposes one, and run a delta round on the same chain after fixes. Report the reviewId chain and a proposed status (`fixed`/`refuted`/`limit`) with an evidence locator for every finding; `resolve` is Main-only. Main does not start a duplicate review of your code: when Main forwards findings or later rounds, you run them.
21
+
18
22
  ## Maker Contract
19
23
 
20
24
  - Inspect existing implementations and callers before editing; reuse established patterns. When a
@@ -1,3 +1,6 @@
1
+ import { lstatSync, readFileSync, realpathSync, statSync } from "node:fs";
2
+ import { homedir } from "node:os";
3
+ import { isAbsolute, join, relative, sep } from "node:path";
1
4
  import type { ExtensionAPI, ExtensionContext } from "@oh-my-pi/pi-coding-agent";
2
5
 
3
6
  export type BuiltinCharacterAlias =
@@ -9,7 +12,8 @@ export type BuiltinCharacterAlias =
9
12
  | "SHION(시온)"
10
13
  | "HIKARI(히카리)";
11
14
 
12
- export type CharacterAlias = BuiltinCharacterAlias;
15
+ /** 내장 7개 + 사무실 모드가 PC 프로필(roster)에서 자리마다 들여오는 동적 alias. */
16
+ export type CharacterAlias = BuiltinCharacterAlias | (string & {});
13
17
 
14
18
  interface OAuthAccountLike {
15
19
  position?: number;
@@ -175,20 +179,203 @@ function buildNameRegexes(pattern: string): { nameInCommand: RegExp; switchComma
175
179
  const QUOTED_SPAN = /```[\s\S]*?```|`[^`\n]*`|'[^'\n]*'|"[^"\n]*"|‘[^’\n]*’|“[^”\n]*”|「[^」\n]*」|『[^』\n]*』|^[ \t]*>.*$/gmu;
176
180
  const SUMMON_MARKER_PREFIX = /\[character-summon\s/u;
177
181
 
178
- const NAME_REGEXES = buildNameRegexes(CHARACTER_NAME_PATTERN);
182
+ // ── 사무실 모드 ───────────────────────────────────────────────────────────────
183
+ // 서버 전체 「기본 / 사무실 모드」. 사무실 모드에서는 roster에 항목이 있는 자리의 캐릭터만 PC 프로필의 것으로
184
+ // 바뀐다(자리의 모델·oauth 위치·toolCapable은 그대로). 모드 파일 `<agentDir>/cuelo-character-mode.json`
185
+ // {"mode":"default"|"office"}과 `<agentDir>/characters-office/roster.json`을 요청마다 mtime 캐시로 읽고,
186
+ // 없거나 깨졌으면 그 자리는 기본이다. 실존 인물에서 온 이름·자산은 저장소가 아니라 프로필에만 둔다.
187
+ interface OfficeSeat {
188
+ seat: BuiltinCharacterAlias;
189
+ alias: string;
190
+ voice: CharacterVoice;
191
+ }
192
+
193
+ interface OfficeState {
194
+ byAlias: ReadonlyMap<string, OfficeSeat>;
195
+ bySeat: ReadonlyMap<BuiltinCharacterAlias, OfficeSeat>;
196
+ /** roster `names`(소문자) → 사무실 alias. 자리의 기본 이름은 NAME_TO_ALIAS가 같은 자리로 푼다. */
197
+ extraNames: ReadonlyMap<string, string>;
198
+ /** 채택한 face 파일과 그 stamp. 파일이 바뀌거나 지워지면 캐시를 다시 만든다. */
199
+ watched: ReadonlyArray<readonly [string, string]>;
200
+ nameInCommand: RegExp;
201
+ switchCommand: RegExp;
202
+ }
203
+
204
+ const DEFAULT_OFFICE_STATE: OfficeState = {
205
+ watched: [],
206
+ byAlias: new Map(),
207
+ bySeat: new Map(),
208
+ extraNames: new Map(),
209
+ ...buildNameRegexes(CHARACTER_NAME_PATTERN),
210
+ };
211
+ const OFFICE_MODE_FILE = "cuelo-character-mode.json";
212
+ const OFFICE_ROSTER_FILE = join("characters-office", "roster.json");
213
+ // alias는 `alias="…"` 속성과 summon marker의 `[^"]+`에 그대로 들어가므로 따옴표(앱 lib/character-mode.ts와 같은 집합)·꺾쇠·줄바꿈을 막는다.
214
+ const OFFICE_ALIAS_UNSAFE = /["'`\u201C\u201D\u2018\u2019<>[\]\r\n]/u;
215
+ const OFFICE_ID = /^[a-z0-9][a-z0-9-]{0,31}$/u;
216
+ /**
217
+ * 사무실 모드가 바꿀 수 있는 자리(MITSU 제외). roster의 다른 자리 항목은 무시한다. 이 순서가 자리 사이 충돌을
218
+ * 가리는 우선순위이며 앱(`lib/character-mode.ts` `OFFICE_SEAT_ALIASES`)과 같아야 한다.
219
+ */
220
+ const OFFICE_SEATS: readonly BuiltinCharacterAlias[] = ["YUKI(유키)", "ISANA(이사나)", "MIO(미오)", "RIN(린)", "SHION(시온)", "HIKARI(히카리)"];
221
+ let officeCache: { key: string; state: OfficeState } | undefined;
222
+
223
+ function officeAgentDir(): string {
224
+ return process.env.PI_CODING_AGENT_DIR || join(homedir(), ".omp", "agent");
225
+ }
226
+
227
+ // 시각·크기만으로는 링크를 같은 크기의 일반 파일로 바꾼 것을 놓친다(Windows 파일 시각은 시스템 tick 단위라 같게 나올 수 있다).
228
+ // 그래서 경로 자체가 링크인지와 대상 inode도 함께 본다.
229
+ function fileStamp(path: string): string {
230
+ try {
231
+ const stat = statSync(path);
232
+ return `${lstatSync(path).isSymbolicLink() ? "l" : "f"}:${stat.ino}:${stat.mtimeMs}:${stat.size}`;
233
+ } catch {
234
+ return "-";
235
+ }
236
+ }
237
+
238
+ function readJsonFile(path: string): unknown {
239
+ try {
240
+ return JSON.parse(readFileSync(path, "utf8").replace(/^\uFEFF/u, ""));
241
+ } catch {
242
+ return undefined;
243
+ }
244
+ }
245
+
246
+ function voiceText(value: unknown): string | undefined {
247
+ if (typeof value !== "string") return undefined;
248
+ const text = value.trim();
249
+ // voice block 경계 태그가 들어 있으면 block을 깨므로 항목째 버린다.
250
+ return text && !/<\/?character-voice/iu.test(text) ? text : undefined;
251
+ }
179
252
 
180
253
  function hasOwn(record: object, key: string): boolean {
181
254
  return Object.prototype.hasOwnProperty.call(record, key);
182
255
  }
183
256
 
184
- /** 내장 alias만 인정한다. 모르는 alias는 undefined. */
185
- function canonicalAlias(alias: string): CharacterAlias | undefined {
186
- return hasOwn(CHARACTER_TARGETS, alias) ? (alias as CharacterAlias) : undefined;
257
+ /** face는 characters-office 안의 실재하는 일반 파일이어야 한다. 안전하면 realpath, 아니면 undefined. */
258
+ function officeFile(dir: string, face: unknown): string | undefined {
259
+ if (typeof face !== "string" || !face.trim() || isAbsolute(face) || /^[A-Za-z]:/u.test(face)) return undefined;
260
+ if (face.split(/[\\/]/u).includes("..")) return undefined;
261
+ try {
262
+ const root = realpathSync(join(dir, "characters-office"));
263
+ const real = realpathSync(join(root, face));
264
+ const inside = relative(root, real);
265
+ if (!inside || inside.startsWith("..") || isAbsolute(inside) || inside.split(sep).includes("..")) return undefined;
266
+ return statSync(real).isFile() ? real : undefined;
267
+ } catch {
268
+ return undefined;
269
+ }
270
+ }
271
+
272
+ function buildOfficeState(dir: string, roster: unknown): OfficeState {
273
+ const seats = roster && typeof roster === "object" ? (roster as { seats?: unknown }).seats : undefined;
274
+ if (!seats || typeof seats !== "object" || Array.isArray(seats)) return DEFAULT_OFFICE_STATE;
275
+ const byAlias = new Map<string, OfficeSeat>();
276
+ const bySeat = new Map<BuiltinCharacterAlias, OfficeSeat>();
277
+ const extraNames = new Map<string, string>();
278
+ const watched: Array<readonly [string, string]> = [];
279
+ const builtinAliases = new Map(Object.keys(CHARACTER_TARGETS).map((builtin) => [builtin.toLocaleLowerCase("en-US"), builtin]));
280
+ // 별칭과 호출 이름은 한 이름 공간(소문자), id는 따로다. 이미 채택된 자리와 하나라도 겹치면 뒤 자리 항목 전체가 무효다.
281
+ const claimedTokens = new Set<string>();
282
+ const claimedIds = new Set<string>();
283
+ for (const seat of OFFICE_SEATS) {
284
+ const entry = hasOwn(seats, seat) ? (seats as Record<string, unknown>)[seat] : undefined;
285
+ if (!entry || typeof entry !== "object") continue;
286
+ const raw = entry as {
287
+ id?: unknown;
288
+ alias?: unknown;
289
+ names?: unknown;
290
+ face?: unknown;
291
+ voice?: { temperament?: unknown; expression?: unknown };
292
+ };
293
+ const alias = typeof raw.alias === "string" ? raw.alias.trim() : "";
294
+ const temperament = voiceText(raw.voice?.temperament);
295
+ const expression = voiceText(raw.voice?.expression);
296
+ const face = officeFile(dir, raw.face);
297
+ // 아직 없거나 무효인 face도 나중에 생기면 항목이 유효해질 수 있어 후보 경로의 stamp를 감시한다.
298
+ if (typeof raw.face === "string" && raw.face.trim() && !isAbsolute(raw.face) && !raw.face.split(/[\\/]/u).includes("..")) {
299
+ const candidate = join(dir, "characters-office", raw.face);
300
+ watched.push([candidate, fileStamp(candidate)]);
301
+ }
302
+ if (
303
+ typeof raw.id !== "string" || !OFFICE_ID.test(raw.id.trim()) ||
304
+ !alias || alias.length > 64 || OFFICE_ALIAS_UNSAFE.test(alias) || hasOwn(CHARACTER_TARGETS, alias) ||
305
+ !temperament || !expression || !face ||
306
+ !Array.isArray(raw.names) || raw.names.length === 0
307
+ ) {
308
+ continue;
309
+ }
310
+ // names: 전부 비지 않은 문자열이어야 하고 다른 자리의 내장 alias·호출 이름이면 항목 전체가 무효다.
311
+ const names: string[] = [];
312
+ let namesValid = true;
313
+ for (const name of raw.names) {
314
+ const lower = typeof name === "string" ? name.trim().toLocaleLowerCase("en-US") : "";
315
+ if (!lower || (builtinAliases.has(lower) && builtinAliases.get(lower) !== seat) || (hasOwn(NAME_TO_ALIAS, lower) && NAME_TO_ALIAS[lower] !== seat)) {
316
+ namesValid = false;
317
+ break;
318
+ }
319
+ names.push(lower);
320
+ }
321
+ if (!namesValid) continue;
322
+ const id = raw.id.trim();
323
+ const tokens = [alias.toLocaleLowerCase("en-US"), ...names];
324
+ if (claimedIds.has(id) || tokens.some((token) => claimedTokens.has(token))) continue;
325
+ claimedIds.add(id);
326
+ for (const token of tokens) claimedTokens.add(token);
327
+ const office: OfficeSeat = { seat, alias, voice: { temperament, expression } };
328
+ byAlias.set(alias, office);
329
+ bySeat.set(seat, office);
330
+ // face 후보 stamp는 위에서 이미 감시 목록에 넣었다.
331
+ // 자리의 기본 이름은 NAME_TO_ALIAS가 이미 같은 자리로 푼다. 사무실 이름은 위에서 자리 사이 유일함이 확인됐다.
332
+ for (const lower of names) if (!hasOwn(NAME_TO_ALIAS, lower)) extraNames.set(lower, alias);
333
+ }
334
+ if (byAlias.size === 0) return { ...DEFAULT_OFFICE_STATE, watched };
335
+ const escaped = [...extraNames.keys()]
336
+ .sort((left, right) => right.length - left.length)
337
+ .map((name) => name.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&"));
338
+ const pattern = escaped.length > 0 ? `(${escaped.join("|")}|${CHARACTER_NAME_PATTERN.slice(1)}` : CHARACTER_NAME_PATTERN;
339
+ return { byAlias, bySeat, extraNames, watched, ...buildNameRegexes(pattern) };
340
+ }
341
+
342
+ function officeState(): OfficeState {
343
+ try {
344
+ const dir = officeAgentDir();
345
+ const modePath = join(dir, OFFICE_MODE_FILE);
346
+ const rosterPath = join(dir, OFFICE_ROSTER_FILE);
347
+ const key = `${dir}\0${fileStamp(modePath)}\0${fileStamp(rosterPath)}`;
348
+ // face 파일이 지워지거나 바뀌면 같은 roster여도 항목이 무효가 될 수 있어 그 stamp도 확인한다.
349
+ if (officeCache?.key === key && officeCache.state.watched.every(([path, stamp]) => fileStamp(path) === stamp)) {
350
+ return officeCache.state;
351
+ }
352
+ const mode = readJsonFile(modePath);
353
+ const office = !!mode && typeof mode === "object" && (mode as { mode?: unknown }).mode === "office";
354
+ // roster도 face와 같이 characters-office 안의 실재 파일일 때만 읽는다(symlink로 밖을 가리키면 사무실 상태 없음).
355
+ const rosterReal = office ? officeFile(dir, "roster.json") : undefined;
356
+ const state = rosterReal ? buildOfficeState(dir, readJsonFile(rosterReal)) : DEFAULT_OFFICE_STATE;
357
+ officeCache = { key, state };
358
+ return state;
359
+ } catch {
360
+ return DEFAULT_OFFICE_STATE;
361
+ }
362
+ }
363
+
364
+ function seatOf(alias: string, state: OfficeState): BuiltinCharacterAlias | undefined {
365
+ const office = state.byAlias.get(alias);
366
+ if (office) return office.seat;
367
+ return hasOwn(CHARACTER_TARGETS, alias) ? (alias as BuiltinCharacterAlias) : undefined;
368
+ }
369
+
370
+ /** 자리(내장 alias)나 사무실 alias를 지금 모드의 화면용 alias로 정규화한다. 모르는 alias는 undefined. */
371
+ function canonicalAlias(alias: string, state: OfficeState): CharacterAlias | undefined {
372
+ const seat = seatOf(alias, state);
373
+ return seat === undefined ? undefined : (state.bySeat.get(seat)?.alias ?? seat);
187
374
  }
188
375
 
189
- /** alias가 가리키는 자리의 모델·oauth 위치·toolCapable. */
376
+ /** alias가 가리키는 자리의 모델·oauth 위치·toolCapable. 사무실 alias도 자리의 target을 쓴다. */
190
377
  export function characterTarget(alias: string): CharacterTarget | undefined {
191
- const seat = canonicalAlias(alias);
378
+ const seat = seatOf(alias, officeState());
192
379
  return seat === undefined ? undefined : CHARACTER_TARGETS[seat];
193
380
  }
194
381
 
@@ -199,18 +386,22 @@ function requireTarget(alias: string): CharacterTarget {
199
386
  }
200
387
 
201
388
  function voiceFor(alias: string): CharacterVoice | undefined {
202
- return hasOwn(CHARACTER_VOICES, alias) ? CHARACTER_VOICES[alias as CharacterAlias] : undefined;
389
+ const state = officeState();
390
+ const office = state.byAlias.get(alias);
391
+ if (office) return office.voice;
392
+ return hasOwn(CHARACTER_VOICES, alias) ? CHARACTER_VOICES[alias as BuiltinCharacterAlias] : undefined;
203
393
  }
204
394
 
205
- function aliasFromName(name: string): CharacterAlias | undefined {
206
- const base = NAME_TO_ALIAS[name.toLocaleLowerCase("en-US")];
207
- return base ? canonicalAlias(base) : undefined;
395
+ function aliasFromName(name: string, state: OfficeState): CharacterAlias | undefined {
396
+ const lower = name.toLocaleLowerCase("en-US");
397
+ const base = state.extraNames.get(lower) ?? NAME_TO_ALIAS[lower];
398
+ return base ? canonicalAlias(base, state) : undefined;
208
399
  }
209
400
 
210
- function aliasesFromCommand(text: string): CharacterAlias[] {
401
+ function aliasesFromCommand(text: string, state: OfficeState): CharacterAlias[] {
211
402
  const aliases = new Set<CharacterAlias>();
212
- for (const match of text.matchAll(NAME_REGEXES.nameInCommand)) {
213
- const alias = aliasFromName(match[1]!);
403
+ for (const match of text.matchAll(state.nameInCommand)) {
404
+ const alias = aliasFromName(match[1]!, state);
214
405
  if (alias) aliases.add(alias);
215
406
  }
216
407
  return [...aliases];
@@ -289,7 +480,7 @@ function injectVoiceIntoSystemBlocks(parts: readonly unknown[], block: string):
289
480
  }
290
481
 
291
482
  function characterAliasForContext(ctx: ExtensionContext, selectedAlias: CharacterAlias | undefined): CharacterAlias | undefined {
292
- const canonical = selectedAlias ? canonicalAlias(selectedAlias) : undefined;
483
+ const canonical = selectedAlias ? canonicalAlias(selectedAlias, officeState()) : undefined;
293
484
  const target = canonical ? characterTarget(canonical) : undefined;
294
485
  if (canonical && target && modelMatches(ctx.model, target.model)) {
295
486
  if (target.oauthPosition === undefined) return canonical;
@@ -386,13 +577,14 @@ async function switchCharacterForSession(
386
577
  }
387
578
 
388
579
  export function parseCharacterIntent(text: string): CharacterIntent | undefined {
389
- const aliases = aliasesFromCommand(text);
580
+ const state = officeState();
581
+ const aliases = aliasesFromCommand(text, state);
390
582
  if (aliases.length === 0) return undefined;
391
583
  if (text.includes("교체")) {
392
584
  // alias도 인용 밖에서만 뽑아 인용 속 이름과 인용 밖 명령어가 결합되지 않게 한다.
393
585
  const command = text.replace(QUOTED_SPAN, " ");
394
- const switchAliases = aliasesFromCommand(command);
395
- if (!NAME_REGEXES.switchCommand.test(command) || switchAliases.length !== 1) return undefined;
586
+ const switchAliases = aliasesFromCommand(command, state);
587
+ if (!state.switchCommand.test(command) || switchAliases.length !== 1) return undefined;
396
588
  return { kind: "switch", alias: switchAliases[0]! };
397
589
  }
398
590
  return SUMMON_ACTION.test(text) ? { kind: "summon", aliases } : undefined;
@@ -400,9 +592,10 @@ export function parseCharacterIntent(text: string): CharacterIntent | undefined
400
592
 
401
593
  /** 다른 input handler가 자연어 summon을 routing prompt로 바꾼 뒤에도 순서와 무관하게 복구한다. */
402
594
  export function parseCharacterSummonDirectives(text: string): CharacterAlias[] {
595
+ const state = officeState();
403
596
  const aliases = new Set<CharacterAlias>();
404
597
  for (const match of text.matchAll(SUMMON_DIRECTIVE)) {
405
- const alias = canonicalAlias(match[1]!);
598
+ const alias = canonicalAlias(match[1]!, state);
406
599
  if (alias) aliases.add(alias);
407
600
  }
408
601
  return [...aliases];
@@ -415,7 +608,7 @@ export function anthropicCharacterForAccounts(
415
608
  const account = accounts[index]!;
416
609
  if (!account.active) continue;
417
610
  const seat = ANTHROPIC_POSITION_CHARACTER[accountPosition(account, index)];
418
- return seat && canonicalAlias(seat);
611
+ return seat && canonicalAlias(seat, officeState());
419
612
  }
420
613
  return undefined;
421
614
  }
@@ -427,7 +620,7 @@ export function characterForProvider(
427
620
  ): CharacterAlias | undefined {
428
621
  if (provider === "anthropic") return anthropicCharacterForAccounts(accounts);
429
622
  const seat = (modelId === undefined ? undefined : MODEL_CHARACTER[`${provider}/${modelId}`]) ?? PROVIDER_CHARACTER[provider];
430
- return seat && canonicalAlias(seat);
623
+ return seat && canonicalAlias(seat, officeState());
431
624
  }
432
625
 
433
626
  export function renderCharacterVoice(alias: CharacterAlias): string {
@@ -622,7 +815,7 @@ export function buildCharacterSummonBrief(brief: string, alias: CharacterAlias):
622
815
 
623
816
  export function parseCharacterSummonMarker(prompt: string): CharacterAlias | undefined {
624
817
  const match = SUMMON_MARKER.exec(prompt);
625
- const alias = match ? canonicalAlias(match[1]!) : undefined;
818
+ const alias = match ? canonicalAlias(match[1]!, officeState()) : undefined;
626
819
  const target = alias === undefined ? undefined : characterTarget(alias);
627
820
  if (!match || alias === undefined || !target || target.model !== match[2]) return undefined;
628
821
  const position = match[3] === undefined ? undefined : Number(match[3]);
@@ -635,8 +828,10 @@ export function parseCharacterSummonMarker(prompt: string): CharacterAlias | und
635
828
  */
636
829
  export function malformedSummonMarkerReason(task: string): string | undefined {
637
830
  if (!SUMMON_MARKER_PREFIX.test(task) || parseCharacterSummonMarker(task) !== undefined) return undefined;
831
+ const state = officeState();
638
832
  const expected = (Object.keys(CHARACTER_TARGETS) as BuiltinCharacterAlias[])
639
833
  .filter((seat) => CHARACTER_TARGETS[seat].toolCapable)
834
+ .map((seat) => state.bySeat.get(seat)?.alias ?? seat)
640
835
  .map((alias) => `- ${alias}: \`${summonMarkerFor(alias)}\``);
641
836
  return [
642
837
  "[CharacterSummonGuard] task의 character-summon marker가 exact target과 맞지 않습니다. " +
@@ -30,7 +30,10 @@ const OPENAI_PROVIDERS: Record<string, true> = { "openai-codex": true, openai: t
30
30
  const GOOGLE_PROVIDERS: Record<string, true> = { "google-vertex": true, google: true, "google-antigravity": true, "google-gemini-cli": true };
31
31
 
32
32
  /** slot이 가리키는 모델을 해석해 한 번 부른다(reasoning high, 10분 제한). 응답이 돌아온 시도는 비용 장부에 남긴다. */
33
- async function completeReviewer(family: Family, prompt: string, ctx: ExtensionContext, signal: AbortSignal): Promise<{ model: string; text: string }> {
33
+ /** system·purpose는 slop_check가 같은 slot 해석·호출·비용 장부 경로를 재사용할 때만 바꾼다. 기본값은 cross_review 그대로다. */
34
+ export async function completeReviewer(
35
+ family: Family, prompt: string, ctx: ExtensionContext, signal: AbortSignal, options: { system?: string; purpose?: string } = {},
36
+ ): Promise<{ model: string; text: string; usage: { input: number; output: number; cacheRead: number; cost: number } }> {
34
37
  const slot = SLOT[family];
35
38
  const [{ findScopedSettings }, { completeSimple }] = await Promise.all([
36
39
  import("@oh-my-pi/pi-coding-agent/config/settings"), import("@oh-my-pi/pi-ai"),
@@ -46,7 +49,7 @@ async function completeReviewer(family: Family, prompt: string, ctx: ExtensionCo
46
49
  const sessionId = ctx.sessionManager.getSessionId();
47
50
  if (!(await ctx.modelRegistry.getApiKey(model, sessionId, { signal }))) throw new ReviewerCallError(`No credentials for ${name}.`, name);
48
51
  const response = await completeSimple(model, {
49
- systemPrompt: [REVIEW_SYSTEM],
52
+ systemPrompt: [options.system ?? REVIEW_SYSTEM],
50
53
  messages: [{ role: "user", content: prompt, timestamp: Date.now() }],
51
54
  }, {
52
55
  apiKey: ctx.modelRegistry.resolver(model, sessionId), sessionId, maxTokens: model.maxTokens ?? undefined,
@@ -55,14 +58,14 @@ async function completeReviewer(family: Family, prompt: string, ctx: ExtensionCo
55
58
  });
56
59
  // 응답이 돌아온 시도는 오류·빈 답도 청구되므로 먼저 장부에 남긴다(skim completeRole과 같다).
57
60
  (ctx.sessionManager as Partial<Pick<SessionManager, "appendModelUsage">>).appendModelUsage?.({
58
- purpose: "cross-review", role: slot, api: model.api, provider: model.provider, model: model.id,
61
+ purpose: options.purpose ?? "cross-review", role: slot, api: model.api, provider: model.provider, model: model.id,
59
62
  usage: response.usage, stopReason: response.stopReason, errorMessage: response.errorMessage,
60
63
  }, { sessionId, parentId: ctx.sessionManager.getLeafId() });
61
64
  const text = response.content.filter((part) => part.type === "text").map((part) => part.text).join("").trim();
62
65
  // 출력 한도·오류로 끝나도 모델이 낸 부분 답은 원문으로 남긴다(통과는 못 한다).
63
66
  if (response.stopReason !== "stop") throw new ReviewerCallError(response.errorMessage ?? `Model stopped: ${response.stopReason}`, name, text || undefined);
64
67
  if (!text) throw new ReviewerCallError(`${name} returned an empty answer.`, name);
65
- return { model: name, text };
68
+ return { model: name, text, usage: { input: response.usage.input, output: response.usage.output, cacheRead: response.usage.cacheRead, cost: response.usage.cost?.total ?? 0 } };
66
69
  }
67
70
 
68
71
  function reviewDeps(ctx: ExtensionContext, signal: AbortSignal): ReviewDeps {