@moxxy/sdk 0.44.0 → 0.45.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 (242) hide show
  1. package/dist/agent-account-limit.d.ts +41 -0
  2. package/dist/agent-account-limit.d.ts.map +1 -0
  3. package/dist/agent-account-limit.js +64 -0
  4. package/dist/agent-account-limit.js.map +1 -0
  5. package/dist/agent-computer-use.d.ts +10 -0
  6. package/dist/agent-computer-use.d.ts.map +1 -0
  7. package/dist/agent-computer-use.js +24 -0
  8. package/dist/agent-computer-use.js.map +1 -0
  9. package/dist/agent-context-window.d.ts +22 -0
  10. package/dist/agent-context-window.d.ts.map +1 -0
  11. package/dist/agent-context-window.js +40 -0
  12. package/dist/agent-context-window.js.map +1 -0
  13. package/dist/agent-no-reply.d.ts +11 -0
  14. package/dist/agent-no-reply.d.ts.map +1 -0
  15. package/dist/agent-no-reply.js +13 -0
  16. package/dist/agent-no-reply.js.map +1 -0
  17. package/dist/agent-step.d.ts +14 -0
  18. package/dist/agent-step.d.ts.map +1 -0
  19. package/dist/agent-step.js +26 -0
  20. package/dist/agent-step.js.map +1 -0
  21. package/dist/agent-work.d.ts +34 -0
  22. package/dist/agent-work.d.ts.map +1 -0
  23. package/dist/agent-work.js +80 -0
  24. package/dist/agent-work.js.map +1 -0
  25. package/dist/attachment-payload.d.ts +1 -1
  26. package/dist/attachment-payload.js +1 -1
  27. package/dist/auto-approve.d.ts +9 -0
  28. package/dist/auto-approve.d.ts.map +1 -1
  29. package/dist/auto-approve.js +18 -0
  30. package/dist/auto-approve.js.map +1 -1
  31. package/dist/channel-auth.d.ts +3 -3
  32. package/dist/channel-auth.d.ts.map +1 -1
  33. package/dist/channel-auth.js +1 -1
  34. package/dist/channel.d.ts +1 -1
  35. package/dist/client-session.d.ts +9 -5
  36. package/dist/client-session.d.ts.map +1 -1
  37. package/dist/computer-control.d.ts +11 -11
  38. package/dist/computer-control.d.ts.map +1 -1
  39. package/dist/computer-control.js +1 -1
  40. package/dist/computer-control.js.map +1 -1
  41. package/dist/conversation-history.d.ts +50 -0
  42. package/dist/conversation-history.d.ts.map +1 -0
  43. package/dist/conversation-history.js +227 -0
  44. package/dist/conversation-history.js.map +1 -0
  45. package/dist/cron.d.ts +58 -0
  46. package/dist/cron.d.ts.map +1 -0
  47. package/dist/cron.js +320 -0
  48. package/dist/cron.js.map +1 -0
  49. package/dist/define.d.ts +2 -1
  50. package/dist/define.d.ts.map +1 -1
  51. package/dist/define.js +1 -0
  52. package/dist/define.js.map +1 -1
  53. package/dist/event-store.d.ts +1 -1
  54. package/dist/event-store.d.ts.map +1 -1
  55. package/dist/event-store.js +1 -0
  56. package/dist/event-store.js.map +1 -1
  57. package/dist/events.d.ts +17 -1
  58. package/dist/events.d.ts.map +1 -1
  59. package/dist/file-diff.d.ts +31 -0
  60. package/dist/file-diff.d.ts.map +1 -0
  61. package/dist/file-diff.js +196 -0
  62. package/dist/file-diff.js.map +1 -0
  63. package/dist/index.d.ts +22 -7
  64. package/dist/index.d.ts.map +1 -1
  65. package/dist/index.js +16 -4
  66. package/dist/index.js.map +1 -1
  67. package/dist/mode/agent-step-loop.d.ts +13 -0
  68. package/dist/mode/agent-step-loop.d.ts.map +1 -0
  69. package/dist/mode/agent-step-loop.js +578 -0
  70. package/dist/mode/agent-step-loop.js.map +1 -0
  71. package/dist/mode/checkpoint-gate.d.ts +25 -0
  72. package/dist/mode/checkpoint-gate.d.ts.map +1 -0
  73. package/dist/mode/checkpoint-gate.js +150 -0
  74. package/dist/mode/checkpoint-gate.js.map +1 -0
  75. package/dist/mode/checkpoint.d.ts +6 -0
  76. package/dist/mode/checkpoint.d.ts.map +1 -1
  77. package/dist/mode/collect-stream.d.ts +2 -0
  78. package/dist/mode/collect-stream.d.ts.map +1 -1
  79. package/dist/mode/collect-stream.js +1 -1
  80. package/dist/mode/collect-stream.js.map +1 -1
  81. package/dist/mode/project-messages.d.ts.map +1 -1
  82. package/dist/mode/project-messages.js +9 -67
  83. package/dist/mode/project-messages.js.map +1 -1
  84. package/dist/mode/react-loop-options.d.ts +113 -0
  85. package/dist/mode/react-loop-options.d.ts.map +1 -0
  86. package/dist/mode/react-loop-options.js +2 -0
  87. package/dist/mode/react-loop-options.js.map +1 -0
  88. package/dist/mode/react-loop.d.ts +2 -99
  89. package/dist/mode/react-loop.d.ts.map +1 -1
  90. package/dist/mode/react-loop.js +7 -145
  91. package/dist/mode/react-loop.js.map +1 -1
  92. package/dist/mode.d.ts +19 -7
  93. package/dist/mode.d.ts.map +1 -1
  94. package/dist/mode.js +1 -1
  95. package/dist/mode.js.map +1 -1
  96. package/dist/npm-registry.d.ts +22 -0
  97. package/dist/npm-registry.d.ts.map +1 -0
  98. package/dist/npm-registry.js +54 -0
  99. package/dist/npm-registry.js.map +1 -0
  100. package/dist/permission.d.ts +31 -0
  101. package/dist/permission.d.ts.map +1 -1
  102. package/dist/process-invocation.d.ts +2 -0
  103. package/dist/process-invocation.d.ts.map +1 -1
  104. package/dist/process-invocation.js +2 -2
  105. package/dist/process-invocation.js.map +1 -1
  106. package/dist/process-tree.d.ts +9 -0
  107. package/dist/process-tree.d.ts.map +1 -0
  108. package/dist/process-tree.js +40 -0
  109. package/dist/process-tree.js.map +1 -0
  110. package/dist/provider-login-bridge.js +1 -1
  111. package/dist/provider-login-bridge.js.map +1 -1
  112. package/dist/provider.d.ts +408 -0
  113. package/dist/provider.d.ts.map +1 -1
  114. package/dist/question-draft.d.ts +19 -0
  115. package/dist/question-draft.d.ts.map +1 -0
  116. package/dist/question-draft.js +30 -0
  117. package/dist/question-draft.js.map +1 -0
  118. package/dist/question.d.ts +204 -0
  119. package/dist/question.d.ts.map +1 -0
  120. package/dist/question.js +141 -0
  121. package/dist/question.js.map +1 -0
  122. package/dist/questions.d.ts +7 -0
  123. package/dist/questions.d.ts.map +1 -0
  124. package/dist/questions.js +7 -0
  125. package/dist/questions.js.map +1 -0
  126. package/dist/resolvers.d.ts +14 -1
  127. package/dist/resolvers.d.ts.map +1 -1
  128. package/dist/resolvers.js +33 -21
  129. package/dist/resolvers.js.map +1 -1
  130. package/dist/schemas.d.ts +53 -53
  131. package/dist/schemas.d.ts.map +1 -1
  132. package/dist/schemas.js +3 -2
  133. package/dist/schemas.js.map +1 -1
  134. package/dist/server.d.ts +2 -0
  135. package/dist/server.d.ts.map +1 -1
  136. package/dist/server.js +3 -0
  137. package/dist/server.js.map +1 -1
  138. package/dist/session-like.d.ts +19 -1
  139. package/dist/session-like.d.ts.map +1 -1
  140. package/dist/starting-model.d.ts +12 -0
  141. package/dist/starting-model.d.ts.map +1 -0
  142. package/dist/starting-model.js +9 -0
  143. package/dist/starting-model.js.map +1 -0
  144. package/dist/tool-dispatch.d.ts.map +1 -1
  145. package/dist/tool-dispatch.js +10 -4
  146. package/dist/tool-dispatch.js.map +1 -1
  147. package/dist/tool-display.d.ts +79 -3
  148. package/dist/tool-display.d.ts.map +1 -1
  149. package/dist/tool-display.js +117 -3
  150. package/dist/tool-display.js.map +1 -1
  151. package/dist/tool-modes.d.ts +1 -1
  152. package/dist/tool-modes.d.ts.map +1 -1
  153. package/dist/tool-modes.js +8 -2
  154. package/dist/tool-modes.js.map +1 -1
  155. package/dist/tool-output.d.ts +15 -0
  156. package/dist/tool-output.d.ts.map +1 -0
  157. package/dist/tool-output.js +39 -0
  158. package/dist/tool-output.js.map +1 -0
  159. package/dist/tool.d.ts +14 -1
  160. package/dist/tool.d.ts.map +1 -1
  161. package/dist/tunnel.d.ts +3 -3
  162. package/dist/tunnel.d.ts.map +1 -1
  163. package/dist/tunnel.js +2 -2
  164. package/dist/workflow-approval.d.ts +9 -9
  165. package/dist/workflow-approval.d.ts.map +1 -1
  166. package/dist/workflow-approval.js +1 -1
  167. package/dist/workflow-approval.js.map +1 -1
  168. package/package.json +33 -5
  169. package/src/agent-account-limit.test.ts +87 -0
  170. package/src/agent-account-limit.ts +89 -0
  171. package/src/agent-computer-use.test.ts +26 -0
  172. package/src/agent-computer-use.ts +29 -0
  173. package/src/agent-context-window.ts +51 -0
  174. package/src/agent-no-reply.ts +16 -0
  175. package/src/agent-step.ts +26 -0
  176. package/src/agent-work.ts +102 -0
  177. package/src/attachment-payload.ts +1 -1
  178. package/src/auto-approve.test.ts +13 -1
  179. package/src/auto-approve.ts +19 -0
  180. package/src/channel-auth.ts +3 -3
  181. package/src/channel.ts +1 -1
  182. package/src/client-session.ts +9 -2
  183. package/src/computer-control.ts +1 -1
  184. package/src/conversation-history.test.ts +184 -0
  185. package/src/conversation-history.ts +272 -0
  186. package/src/cron.test.ts +280 -0
  187. package/src/cron.ts +355 -0
  188. package/src/define.test.ts +1 -1
  189. package/src/define.ts +3 -1
  190. package/src/event-store.ts +2 -1
  191. package/src/events.ts +17 -1
  192. package/src/file-diff.test.ts +103 -0
  193. package/src/file-diff.ts +208 -0
  194. package/src/index.ts +121 -1
  195. package/src/json-file-store.test.ts +1 -1
  196. package/src/loop-helpers.test.ts +15 -0
  197. package/src/mode/agent-step-loop.ts +645 -0
  198. package/src/mode/checkpoint-gate.ts +198 -0
  199. package/src/mode/checkpoint.ts +6 -0
  200. package/src/mode/collect-stream.ts +1 -1
  201. package/src/mode/project-messages.test.ts +11 -0
  202. package/src/mode/project-messages.ts +8 -61
  203. package/src/mode/react-loop-options.ts +124 -0
  204. package/src/mode/react-loop.ts +10 -296
  205. package/src/mode.ts +19 -7
  206. package/src/npm-registry.test.ts +95 -0
  207. package/src/npm-registry.ts +57 -0
  208. package/src/permission.ts +30 -0
  209. package/src/process-invocation.test.ts +25 -0
  210. package/src/process-invocation.ts +8 -3
  211. package/src/process-tree.ts +37 -0
  212. package/src/provider-login-bridge.ts +1 -1
  213. package/src/provider-schema-constraints.test.ts +1 -1
  214. package/src/provider-utils.test.ts +1 -1
  215. package/src/provider.ts +346 -0
  216. package/src/question-draft.test.ts +57 -0
  217. package/src/question-draft.ts +47 -0
  218. package/src/question.test.ts +130 -0
  219. package/src/question.ts +229 -0
  220. package/src/questions.ts +6 -0
  221. package/src/resolvers.test.ts +84 -1
  222. package/src/resolvers.ts +40 -27
  223. package/src/schemas.test.ts +5 -0
  224. package/src/schemas.ts +3 -2
  225. package/src/server.ts +3 -0
  226. package/src/session-like.ts +19 -1
  227. package/src/skill-mentions.test.ts +1 -1
  228. package/src/starting-model.test.ts +19 -0
  229. package/src/starting-model.ts +11 -0
  230. package/src/token-efficiency.test.ts +1 -1
  231. package/src/tool-dispatch.ts +10 -4
  232. package/src/tool-display.test.ts +119 -0
  233. package/src/tool-display.ts +162 -4
  234. package/src/tool-gating.test.ts +1 -1
  235. package/src/tool-icon.test.ts +1 -1
  236. package/src/tool-modes.test.ts +18 -1
  237. package/src/tool-modes.ts +6 -2
  238. package/src/tool-output.test.ts +46 -0
  239. package/src/tool-output.ts +46 -0
  240. package/src/tool.ts +14 -1
  241. package/src/tunnel.ts +3 -3
  242. package/src/workflow-approval.ts +1 -1
@@ -19,7 +19,7 @@ export interface WorkflowApprovalsView {
19
19
  revoke(id: string): Promise<void>;
20
20
  cancel(id: string): Promise<void>;
21
21
  }
22
- import { z } from 'zod';
22
+ import { z } from 'zod/v3';
23
23
  export declare const workflowApprovalItemSchema: z.ZodEffects<z.ZodObject<{
24
24
  id: z.ZodString;
25
25
  workflowId: z.ZodString;
@@ -34,7 +34,7 @@ export declare const workflowApprovalItemSchema: z.ZodEffects<z.ZodObject<{
34
34
  }, "strict", z.ZodTypeAny, {
35
35
  tool: string;
36
36
  id: string;
37
- status: "denied" | "pending" | "allowed" | "cancelled" | "interrupted" | "revoked";
37
+ status: "denied" | "pending" | "cancelled" | "allowed" | "interrupted" | "revoked";
38
38
  workflowId: string;
39
39
  workflowName: string;
40
40
  revision: string;
@@ -45,7 +45,7 @@ export declare const workflowApprovalItemSchema: z.ZodEffects<z.ZodObject<{
45
45
  }, {
46
46
  tool: string;
47
47
  id: string;
48
- status: "denied" | "pending" | "allowed" | "cancelled" | "interrupted" | "revoked";
48
+ status: "denied" | "pending" | "cancelled" | "allowed" | "interrupted" | "revoked";
49
49
  workflowId: string;
50
50
  workflowName: string;
51
51
  revision: string;
@@ -57,7 +57,7 @@ export declare const workflowApprovalItemSchema: z.ZodEffects<z.ZodObject<{
57
57
  input: unknown;
58
58
  tool: string;
59
59
  id: string;
60
- status: "denied" | "pending" | "allowed" | "cancelled" | "interrupted" | "revoked";
60
+ status: "denied" | "pending" | "cancelled" | "allowed" | "interrupted" | "revoked";
61
61
  workflowId: string;
62
62
  workflowName: string;
63
63
  revision: string;
@@ -67,7 +67,7 @@ export declare const workflowApprovalItemSchema: z.ZodEffects<z.ZodObject<{
67
67
  }, {
68
68
  tool: string;
69
69
  id: string;
70
- status: "denied" | "pending" | "allowed" | "cancelled" | "interrupted" | "revoked";
70
+ status: "denied" | "pending" | "cancelled" | "allowed" | "interrupted" | "revoked";
71
71
  workflowId: string;
72
72
  workflowName: string;
73
73
  revision: string;
@@ -90,7 +90,7 @@ export declare const workflowApprovalItemsSchema: z.ZodArray<z.ZodEffects<z.ZodO
90
90
  }, "strict", z.ZodTypeAny, {
91
91
  tool: string;
92
92
  id: string;
93
- status: "denied" | "pending" | "allowed" | "cancelled" | "interrupted" | "revoked";
93
+ status: "denied" | "pending" | "cancelled" | "allowed" | "interrupted" | "revoked";
94
94
  workflowId: string;
95
95
  workflowName: string;
96
96
  revision: string;
@@ -101,7 +101,7 @@ export declare const workflowApprovalItemsSchema: z.ZodArray<z.ZodEffects<z.ZodO
101
101
  }, {
102
102
  tool: string;
103
103
  id: string;
104
- status: "denied" | "pending" | "allowed" | "cancelled" | "interrupted" | "revoked";
104
+ status: "denied" | "pending" | "cancelled" | "allowed" | "interrupted" | "revoked";
105
105
  workflowId: string;
106
106
  workflowName: string;
107
107
  revision: string;
@@ -113,7 +113,7 @@ export declare const workflowApprovalItemsSchema: z.ZodArray<z.ZodEffects<z.ZodO
113
113
  input: unknown;
114
114
  tool: string;
115
115
  id: string;
116
- status: "denied" | "pending" | "allowed" | "cancelled" | "interrupted" | "revoked";
116
+ status: "denied" | "pending" | "cancelled" | "allowed" | "interrupted" | "revoked";
117
117
  workflowId: string;
118
118
  workflowName: string;
119
119
  revision: string;
@@ -123,7 +123,7 @@ export declare const workflowApprovalItemsSchema: z.ZodArray<z.ZodEffects<z.ZodO
123
123
  }, {
124
124
  tool: string;
125
125
  id: string;
126
- status: "denied" | "pending" | "allowed" | "cancelled" | "interrupted" | "revoked";
126
+ status: "denied" | "pending" | "cancelled" | "allowed" | "interrupted" | "revoked";
127
127
  workflowId: string;
128
128
  workflowName: string;
129
129
  revision: string;
@@ -1 +1 @@
1
- {"version":3,"file":"workflow-approval.d.ts","sourceRoot":"","sources":["../src/workflow-approval.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,MAAM,sBAAsB,GAAG,YAAY,GAAG,cAAc,GAAG,MAAM,CAAC;AAC5E,MAAM,WAAW,oBAAqB,SAAQ,qBAAqB;IACjE,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;IACxB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,MAAM,EAAE,SAAS,GAAG,SAAS,GAAG,QAAQ,GAAG,WAAW,GAAG,aAAa,GAAG,SAAS,CAAC;IAC5F,QAAQ,CAAC,MAAM,CAAC,EAAE,sBAAsB,CAAC;CAC1C;AAED,MAAM,WAAW,qBAAqB;IACpC,IAAI,IAAI,OAAO,CAAC,aAAa,CAAC,oBAAoB,CAAC,CAAC,CAAC;IACrD,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,sBAAsB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAClE,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAClC,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACnC;AACD,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,eAAO,MAAM,0BAA0B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAciB,CAAC;AACzD,eAAO,MAAM,2BAA2B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;WAAsC,CAAC"}
1
+ {"version":3,"file":"workflow-approval.d.ts","sourceRoot":"","sources":["../src/workflow-approval.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,MAAM,sBAAsB,GAAG,YAAY,GAAG,cAAc,GAAG,MAAM,CAAC;AAC5E,MAAM,WAAW,oBAAqB,SAAQ,qBAAqB;IACjE,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;IACxB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,MAAM,EAAE,SAAS,GAAG,SAAS,GAAG,QAAQ,GAAG,WAAW,GAAG,aAAa,GAAG,SAAS,CAAC;IAC5F,QAAQ,CAAC,MAAM,CAAC,EAAE,sBAAsB,CAAC;CAC1C;AAED,MAAM,WAAW,qBAAqB;IACpC,IAAI,IAAI,OAAO,CAAC,aAAa,CAAC,oBAAoB,CAAC,CAAC,CAAC;IACrD,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,sBAAsB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAClE,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAClC,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACnC;AACD,OAAO,EAAE,CAAC,EAAE,MAAM,QAAQ,CAAC;AAE3B,eAAO,MAAM,0BAA0B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAciB,CAAC;AACzD,eAAO,MAAM,2BAA2B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;WAAsC,CAAC"}
@@ -1,4 +1,4 @@
1
- import { z } from 'zod';
1
+ import { z } from 'zod/v3';
2
2
  export const workflowApprovalItemSchema = z
3
3
  .object({
4
4
  id: z.string().uuid(),
@@ -1 +1 @@
1
- {"version":3,"file":"workflow-approval.js","sourceRoot":"","sources":["../src/workflow-approval.ts"],"names":[],"mappings":"AAuBA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,MAAM,CAAC,MAAM,0BAA0B,GAAG,CAAC;KACxC,MAAM,CAAC;IACN,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE;IACrB,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IAC7B,YAAY,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IAC/B,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IAC3B,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IACxB,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IACvB,KAAK,EAAE,CAAC,CAAC,OAAO,EAAE;IAClB,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IACzC,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,SAAS,EAAE,SAAS,EAAE,QAAQ,EAAE,WAAW,EAAE,aAAa,EAAE,SAAS,CAAC,CAAC;IACvF,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,YAAY,EAAE,cAAc,EAAE,MAAM,CAAC,CAAC,CAAC,QAAQ,EAAE;CAClE,CAAC;KACD,MAAM,EAAE;KACR,SAAS,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,EAAE,GAAG,IAAI,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;AACzD,MAAM,CAAC,MAAM,2BAA2B,GAAG,CAAC,CAAC,KAAK,CAAC,0BAA0B,CAAC,CAAC"}
1
+ {"version":3,"file":"workflow-approval.js","sourceRoot":"","sources":["../src/workflow-approval.ts"],"names":[],"mappings":"AAuBA,OAAO,EAAE,CAAC,EAAE,MAAM,QAAQ,CAAC;AAE3B,MAAM,CAAC,MAAM,0BAA0B,GAAG,CAAC;KACxC,MAAM,CAAC;IACN,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE;IACrB,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IAC7B,YAAY,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IAC/B,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IAC3B,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IACxB,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IACvB,KAAK,EAAE,CAAC,CAAC,OAAO,EAAE;IAClB,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IACzC,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,SAAS,EAAE,SAAS,EAAE,QAAQ,EAAE,WAAW,EAAE,aAAa,EAAE,SAAS,CAAC,CAAC;IACvF,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,YAAY,EAAE,cAAc,EAAE,MAAM,CAAC,CAAC,CAAC,QAAQ,EAAE;CAClE,CAAC;KACD,MAAM,EAAE;KACR,SAAS,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,EAAE,GAAG,IAAI,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;AACzD,MAAM,CAAC,MAAM,2BAA2B,GAAG,CAAC,CAAC,KAAK,CAAC,0BAA0B,CAAC,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@moxxy/sdk",
3
- "version": "0.44.0",
3
+ "version": "0.45.0",
4
4
  "description": "Typed public surface for the moxxy framework: event types, define* factories, lifecycle hook signatures.",
5
5
  "keywords": [
6
6
  "moxxy",
@@ -53,6 +53,30 @@
53
53
  "types": "./dist/tool-display.d.ts",
54
54
  "import": "./dist/tool-display.js"
55
55
  },
56
+ "./agent-account-limit": {
57
+ "types": "./dist/agent-account-limit.d.ts",
58
+ "import": "./dist/agent-account-limit.js"
59
+ },
60
+ "./agent-no-reply": {
61
+ "types": "./dist/agent-no-reply.d.ts",
62
+ "import": "./dist/agent-no-reply.js"
63
+ },
64
+ "./starting-model": {
65
+ "types": "./dist/starting-model.d.ts",
66
+ "import": "./dist/starting-model.js"
67
+ },
68
+ "./agent-work": {
69
+ "types": "./dist/agent-work.d.ts",
70
+ "import": "./dist/agent-work.js"
71
+ },
72
+ "./cron": {
73
+ "types": "./dist/cron.d.ts",
74
+ "import": "./dist/cron.js"
75
+ },
76
+ "./agent-computer-use": {
77
+ "types": "./dist/agent-computer-use.d.ts",
78
+ "import": "./dist/agent-computer-use.js"
79
+ },
56
80
  "./attachment-payload": {
57
81
  "types": "./dist/attachment-payload.d.ts",
58
82
  "import": "./dist/attachment-payload.js"
@@ -68,6 +92,10 @@
68
92
  "./transcriber": {
69
93
  "types": "./dist/transcriber.d.ts",
70
94
  "import": "./dist/transcriber.js"
95
+ },
96
+ "./questions": {
97
+ "types": "./dist/questions.d.ts",
98
+ "import": "./dist/questions.js"
71
99
  }
72
100
  },
73
101
  "files": [
@@ -79,14 +107,14 @@
79
107
  "access": "public"
80
108
  },
81
109
  "peerDependencies": {
82
- "zod": "^3.25.76",
83
- "undici": "^8.11.2"
110
+ "zod": "^4.6.6",
111
+ "undici": "^8.12.2"
84
112
  },
85
113
  "devDependencies": {
86
114
  "typescript": "^6.0.3",
87
- "undici": "^8.11.2",
115
+ "undici": "^8.12.2",
88
116
  "vitest": "^5.0.3",
89
- "zod": "^3.25.76",
117
+ "zod": "^4.6.6",
90
118
  "@moxxy/tsconfig": "0.0.0",
91
119
  "@moxxy/vitest-preset": "0.0.0"
92
120
  },
@@ -0,0 +1,87 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import {
3
+ AGENT_ACCOUNT_LIMIT_SUBTYPE,
4
+ accountLimitInForce,
5
+ accountLimitOfEvent,
6
+ recordedAccountLimit,
7
+ sameAccountLimit,
8
+ withKnownReset,
9
+ type AgentAccountLimit,
10
+ } from './agent-account-limit.js';
11
+ import { AGENT_CONNECTION_PLUGIN_ID } from './agent-step.js';
12
+ import type { MoxxyEvent } from './events.js';
13
+
14
+ function recorded(provider: string, limit: AgentAccountLimit, agent?: string): MoxxyEvent {
15
+ return {
16
+ type: 'plugin_event',
17
+ pluginId: AGENT_CONNECTION_PLUGIN_ID,
18
+ subtype: AGENT_ACCOUNT_LIMIT_SUBTYPE,
19
+ payload: { provider, ...(agent ? { agent } : {}), limit },
20
+ } as unknown as MoxxyEvent;
21
+ }
22
+
23
+ const HOUR = 3_600_000;
24
+
25
+ describe('recordedAccountLimit', () => {
26
+ it('reads the last limit recorded for the provider', () => {
27
+ const events = [
28
+ recorded('claude-agent', { state: 'near', usedPercent: 80 }),
29
+ recorded('codex-agent', { state: 'ok', usedPercent: 10 }),
30
+ recorded('claude-agent', { state: 'reached', resetsAt: 5 * HOUR }),
31
+ ];
32
+ expect(recordedAccountLimit(events, 'claude-agent')).toEqual({ provider: 'claude-agent', limit: { state: 'reached', resetsAt: 5 * HOUR } });
33
+ expect(recordedAccountLimit(events, 'codex-agent')).toEqual({ provider: 'codex-agent', limit: { state: 'ok', usedPercent: 10 } });
34
+ expect(recordedAccountLimit(events, 'opencode')).toBeUndefined();
35
+ });
36
+ });
37
+
38
+ describe('accountLimitOfEvent', () => {
39
+ it('reads the limit and whose it is from the event that records it, and nothing from any other', () => {
40
+ expect(accountLimitOfEvent(recorded('claude-agent', { state: 'reached' }, 'Claude'))).toEqual({
41
+ provider: 'claude-agent',
42
+ agent: 'Claude',
43
+ limit: { state: 'reached' },
44
+ });
45
+ expect(accountLimitOfEvent({ type: 'plugin_event', pluginId: AGENT_CONNECTION_PLUGIN_ID, subtype: 'goal_status', payload: {} } as unknown as MoxxyEvent)).toBeUndefined();
46
+ });
47
+ });
48
+
49
+ describe('accountLimitInForce', () => {
50
+ it('holds a reached or near limit until its window resets', () => {
51
+ const reached = { provider: 'claude-agent', limit: { state: 'reached', resetsAt: 5 * HOUR } } as const;
52
+ expect(accountLimitInForce(reached, 4 * HOUR)).toBe(reached);
53
+ expect(accountLimitInForce(reached, 5 * HOUR)).toBeUndefined();
54
+ const near = { provider: 'claude-agent', limit: { state: 'near', usedPercent: 92 } } as const;
55
+ expect(accountLimitInForce(near, 4 * HOUR)).toBe(near);
56
+ });
57
+
58
+ it('says nothing about a limit with room left', () => {
59
+ expect(accountLimitInForce({ provider: 'codex-agent', limit: { state: 'ok', usedPercent: 10 } }, 0)).toBeUndefined();
60
+ expect(accountLimitInForce(undefined, 0)).toBeUndefined();
61
+ });
62
+ });
63
+
64
+ describe('sameAccountLimit', () => {
65
+ it('ignores what a person would not see change: a fraction of a percent, seconds of the reset', () => {
66
+ const limit: AgentAccountLimit = { state: 'near', usedPercent: 91.2, resetsAt: 5 * HOUR, windowMinutes: 10080 };
67
+ expect(sameAccountLimit(limit, { ...limit, usedPercent: 91.4, resetsAt: 5 * HOUR + 20_000 })).toBe(true);
68
+ expect(sameAccountLimit(limit, { ...limit, usedPercent: 92 })).toBe(false);
69
+ expect(sameAccountLimit(limit, { ...limit, state: 'reached' })).toBe(false);
70
+ expect(sameAccountLimit(undefined, limit)).toBe(false);
71
+ });
72
+ });
73
+
74
+ describe('a used-up limit reported without its reset', () => {
75
+ const now = 1_000_000;
76
+ const weekly = { state: 'reached', windowMinutes: 10080, resetsAt: now + 60_000 } as const;
77
+
78
+ it('keeps the reset known before while it lies ahead', () => {
79
+ expect(withKnownReset({ state: 'reached' }, weekly, now)).toEqual(weekly);
80
+ });
81
+
82
+ it('stays as the agent said once that reset has passed, or when the agent gave its own', () => {
83
+ expect(withKnownReset({ state: 'reached' }, weekly, now + 120_000)).toEqual({ state: 'reached' });
84
+ expect(withKnownReset({ state: 'reached', resetsAt: now + 5 }, weekly, now)).toEqual({ state: 'reached', resetsAt: now + 5 });
85
+ expect(withKnownReset({ state: 'near', usedPercent: 80 }, weekly, now)).toEqual({ state: 'near', usedPercent: 80 });
86
+ });
87
+ });
@@ -0,0 +1,89 @@
1
+ import { AGENT_CONNECTION_PLUGIN_ID } from './agent-step.js';
2
+ import type { MoxxyEvent } from './events.js';
3
+
4
+ /**
5
+ * How much of the person's account an agent connection's agent may still use, as the agent
6
+ * reports it (Claude's plan limits, Codex's rate limits). Recorded in the log, so every surface
7
+ * reads the same state.
8
+ */
9
+ export interface AgentAccountLimit {
10
+ /** `reached`: the agent does no work until `resetsAt`; `near`: most of the window is used. */
11
+ readonly state: 'ok' | 'near' | 'reached';
12
+ /** When the window resets, in epoch ms, when the agent says. */
13
+ readonly resetsAt?: number;
14
+ /** The window's length in minutes: 300 for five hours, 10080 for a week. */
15
+ readonly windowMinutes?: number;
16
+ /** How much of the window is used, 0–100. */
17
+ readonly usedPercent?: number;
18
+ }
19
+
20
+ /** From how much of a window a limit counts as near, for an agent that does not say so itself (Claude warns from 75%). */
21
+ export const ACCOUNT_LIMIT_NEAR_PERCENT = 75;
22
+
23
+ /** Where an agent connection records its agent's account limit, under `AGENT_CONNECTION_PLUGIN_ID`. */
24
+ export const AGENT_ACCOUNT_LIMIT_SUBTYPE = 'account_limit';
25
+
26
+ export interface RecordedAccountLimit {
27
+ readonly provider: string;
28
+ /** The agent's name ("Claude", "Codex"), for the surfaces to say whose limit it is. */
29
+ readonly agent?: string;
30
+ readonly limit: AgentAccountLimit;
31
+ }
32
+
33
+ /** The last account limit recorded for the provider. */
34
+ export function recordedAccountLimit(events: ReadonlyArray<MoxxyEvent>, provider: string): RecordedAccountLimit | undefined {
35
+ for (let i = events.length - 1; i >= 0; i -= 1) {
36
+ const event = events[i];
37
+ if (!event || event.type !== 'plugin_event') continue;
38
+ if (event.pluginId !== AGENT_CONNECTION_PLUGIN_ID || event.subtype !== AGENT_ACCOUNT_LIMIT_SUBTYPE) continue;
39
+ const payload = event.payload as Partial<RecordedAccountLimit> | null;
40
+ if (payload && payload.provider === provider && payload.limit) return accountLimitPayload(payload);
41
+ }
42
+ return undefined;
43
+ }
44
+
45
+ /** The limit a plugin event records, or undefined when it records something else. */
46
+ export function accountLimitOfEvent(event: MoxxyEvent): RecordedAccountLimit | undefined {
47
+ if (event.type !== 'plugin_event' || event.pluginId !== AGENT_CONNECTION_PLUGIN_ID || event.subtype !== AGENT_ACCOUNT_LIMIT_SUBTYPE) return undefined;
48
+ const payload = event.payload as Partial<RecordedAccountLimit> | null;
49
+ return payload && typeof payload.provider === 'string' && payload.limit ? accountLimitPayload(payload) : undefined;
50
+ }
51
+
52
+ function accountLimitPayload(payload: Partial<RecordedAccountLimit>): RecordedAccountLimit {
53
+ return {
54
+ provider: String(payload.provider),
55
+ ...(typeof payload.agent === 'string' ? { agent: payload.agent } : {}),
56
+ limit: payload.limit as AgentAccountLimit,
57
+ };
58
+ }
59
+
60
+ /** A near or reached limit, until its window resets; a limit with room left says nothing. */
61
+ export function accountLimitInForce(recorded: RecordedAccountLimit | undefined, nowMs: number): RecordedAccountLimit | undefined {
62
+ if (!recorded || recorded.limit.state === 'ok') return undefined;
63
+ const { resetsAt } = recorded.limit;
64
+ return resetsAt !== undefined && resetsAt <= nowMs ? undefined : recorded;
65
+ }
66
+
67
+ /** Whether two reports would read the same to the person: whole percents, the reset to the minute. */
68
+ export function sameAccountLimit(a: AgentAccountLimit | undefined, b: AgentAccountLimit): boolean {
69
+ if (!a) return false;
70
+ const minute = (ms: number | undefined) => (ms === undefined ? undefined : Math.round(ms / 60_000));
71
+ const percent = (p: number | undefined) => (p === undefined ? undefined : Math.floor(p));
72
+ return (
73
+ a.state === b.state &&
74
+ a.windowMinutes === b.windowMinutes &&
75
+ minute(a.resetsAt) === minute(b.resetsAt) &&
76
+ percent(a.usedPercent) === percent(b.usedPercent)
77
+ );
78
+ }
79
+
80
+ /**
81
+ * A used-up limit the agent reports without its reset (Claude Code's own
82
+ * "You've hit your limit" when its process already said when it resets) keeps
83
+ * the reset known before, while that still lies ahead.
84
+ */
85
+ export function withKnownReset(reading: AgentAccountLimit, known: AgentAccountLimit | undefined, nowMs: number): AgentAccountLimit {
86
+ if (reading.state !== 'reached' || reading.resetsAt !== undefined) return reading;
87
+ if (!known || known.resetsAt === undefined || known.resetsAt <= nowMs) return reading;
88
+ return { ...reading, resetsAt: known.resetsAt, ...(known.windowMinutes !== undefined && reading.windowMinutes === undefined ? { windowMinutes: known.windowMinutes } : {}) };
89
+ }
@@ -0,0 +1,26 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { agentComputerUseOf } from './agent-computer-use.js';
3
+
4
+ describe("an agent's own tool call that controls the computer", () => {
5
+ it('is a call to a computer-use MCP server, with the app it names', () => {
6
+ expect(agentComputerUseOf('mcp__computer-use__click', { app: 'Arc', element_index: '185' })).toEqual({ app: 'Arc' });
7
+ expect(agentComputerUseOf('mcp__computer-use__list_apps', {})).toEqual({ app: null });
8
+ expect(agentComputerUseOf('mcp__computer_use__screenshot', { app: 'Safari' })).toEqual({ app: 'Safari' });
9
+ expect(agentComputerUseOf('computer-use_click', { app: 'Notes' })).toEqual({ app: 'Notes' });
10
+ });
11
+
12
+ it("is Codex's computer JavaScript, with the app the code opens", () => {
13
+ expect(agentComputerUseOf('mcp__cua_repl__js', { code: "const app = await cua.getApp('Electron'); await app.getAXState();" })).toEqual({ app: 'Electron' });
14
+ expect(agentComputerUseOf('mcp__cua_repl__js', { code: 'await cua.getState();' })).toEqual({ app: null });
15
+ });
16
+
17
+ it("is not Codex's own browser, which does not touch the person's apps", () => {
18
+ expect(agentComputerUseOf('mcp__cua_repl__js', { code: "const tab = await cua.createBrowserTab('https://example.com');" })).toBeUndefined();
19
+ });
20
+
21
+ it('is not any other tool', () => {
22
+ expect(agentComputerUseOf('Bash', { command: 'open -a Safari' })).toBeUndefined();
23
+ expect(agentComputerUseOf('mcp__docs__search', { app: 'Arc' })).toBeUndefined();
24
+ expect(agentComputerUseOf('computer_click', { app: 'Arc' })).toBeUndefined();
25
+ });
26
+ });
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Whether a tool call an agent connection ran itself operates the person's computer, recognised
3
+ * by the tool's name, so every surface can say the agent is using the computer and offer Stop.
4
+ */
5
+
6
+ export interface AgentComputerUse {
7
+ /** The app the call names, `null` when it names none (listing apps, a screenshot of the screen). */
8
+ readonly app: string | null;
9
+ }
10
+
11
+ /**
12
+ * A computer-use MCP server's tools, which drive the person's apps: `mcp__computer-use__click` from
13
+ * Claude Code and Codex, `computer-use_click` from OpenCode (which names MCP tools `server_tool`).
14
+ */
15
+ const COMPUTER_SERVERS = /^(?:mcp__)?computer[-_]use_/;
16
+ /** Codex's computer use as JavaScript against its `cua` API; the same API also drives Codex's own browser. */
17
+ const CUA_JS = 'mcp__cua_repl__js';
18
+ const CUA_APPS = /\bcua\.(getApp|getState|listApps|listWindows)\s*\(/;
19
+ const CUA_APP_NAME = /\bcua\.getApp\(\s*['"`]([^'"`]+)['"`]/;
20
+
21
+ export function agentComputerUseOf(name: string, input: unknown): AgentComputerUse | undefined {
22
+ const fields = typeof input === 'object' && input !== null ? (input as Record<string, unknown>) : {};
23
+ if (COMPUTER_SERVERS.test(name)) {
24
+ return { app: typeof fields.app === 'string' && fields.app.length > 0 ? fields.app : null };
25
+ }
26
+ if (name !== CUA_JS || typeof fields.code !== 'string' || !CUA_APPS.test(fields.code)) return undefined;
27
+ const named = CUA_APP_NAME.exec(fields.code);
28
+ return { app: named && named[1] ? named[1] : null };
29
+ }
@@ -0,0 +1,51 @@
1
+ import { AGENT_CONNECTION_PLUGIN_ID } from './agent-step.js';
2
+ import type { MoxxyEvent } from './events.js';
3
+ import type { ModelDescriptor } from './provider.js';
4
+
5
+ /**
6
+ * Where an agent connection records the context window its agent reports for a model
7
+ * (Codex's `modelContextWindow`, Claude's `modelUsage.contextWindow`), under
8
+ * `AGENT_CONNECTION_PLUGIN_ID`. It replaces the starting window from the model list,
9
+ * so every surface shows the window the agent really runs with.
10
+ */
11
+ export const AGENT_CONTEXT_WINDOW_SUBTYPE = 'context_window';
12
+
13
+ export interface RecordedContextWindow {
14
+ readonly provider: string;
15
+ readonly model: string;
16
+ readonly tokens: number;
17
+ }
18
+
19
+ export function isContextWindowSize(tokens: unknown): tokens is number {
20
+ return typeof tokens === 'number' && Number.isSafeInteger(tokens) && tokens > 0;
21
+ }
22
+
23
+ /** The window an event records, or undefined when it records something else. */
24
+ export function contextWindowOfEvent(event: MoxxyEvent): RecordedContextWindow | undefined {
25
+ if (event.type !== 'plugin_event' || event.pluginId !== AGENT_CONNECTION_PLUGIN_ID || event.subtype !== AGENT_CONTEXT_WINDOW_SUBTYPE) return undefined;
26
+ const payload = event.payload as Partial<RecordedContextWindow> | null;
27
+ if (!payload || typeof payload.provider !== 'string' || typeof payload.model !== 'string' || !isContextWindowSize(payload.tokens)) return undefined;
28
+ return { provider: payload.provider, model: payload.model, tokens: payload.tokens };
29
+ }
30
+
31
+ /** The last window recorded for each model of the provider. */
32
+ export function recordedContextWindows(events: ReadonlyArray<MoxxyEvent>, provider: string): ReadonlyMap<string, number> {
33
+ const windows = new Map<string, number>();
34
+ for (const event of events) {
35
+ const recorded = contextWindowOfEvent(event);
36
+ if (recorded && recorded.provider === provider) windows.set(recorded.model, recorded.tokens);
37
+ }
38
+ return windows;
39
+ }
40
+
41
+ /** The models with the windows the agent reported in place of their starting ones. */
42
+ export function withRecordedContextWindows(
43
+ models: ReadonlyArray<ModelDescriptor>,
44
+ windows: ReadonlyMap<string, number>,
45
+ ): ReadonlyArray<ModelDescriptor> {
46
+ if (windows.size === 0) return models;
47
+ return models.map((model) => {
48
+ const tokens = windows.get(model.id);
49
+ return tokens === undefined || tokens === model.contextWindow ? model : { ...model, contextWindow: tokens };
50
+ });
51
+ }
@@ -0,0 +1,16 @@
1
+ import { AGENT_CONNECTION_PLUGIN_ID } from './agent-step.js';
2
+ import type { MoxxyEvent } from './events.js';
3
+
4
+ /**
5
+ * Recorded under `AGENT_CONNECTION_PLUGIN_ID` at the end of a turn the agent took up by
6
+ * itself ("Agent continued") in which it said nothing and ran nothing, so every surface
7
+ * says so instead of leaving the turn's marker alone in the chat.
8
+ */
9
+ export const AGENT_NO_REPLY_SUBTYPE = 'no_reply';
10
+
11
+ /** How every surface words the note. */
12
+ export const AGENT_NO_REPLY_TEXT = 'The agent finished without replying.';
13
+
14
+ export function isAgentNoReplyEvent(event: MoxxyEvent): boolean {
15
+ return event.type === 'plugin_event' && event.pluginId === AGENT_CONNECTION_PLUGIN_ID && event.subtype === AGENT_NO_REPLY_SUBTYPE;
16
+ }
@@ -0,0 +1,26 @@
1
+ import type { MoxxyEvent } from './events.js';
2
+ import { asPluginId } from './ids.js';
3
+
4
+ /**
5
+ * Where an agent connection keeps the vendor agent's session in the log. The
6
+ * `agentStep` contract itself is part of the provider types (`provider.ts`).
7
+ */
8
+
9
+ export const AGENT_CONNECTION_PLUGIN_ID = asPluginId('@moxxy/agent-connection');
10
+ export const AGENT_SESSION_SUBTYPE = 'agent_session';
11
+ /** The state of the agent's own goal (Claude `/goal`, Codex `thread/goal`), recorded after its turn. */
12
+ export const AGENT_GOAL_SUBTYPE = 'goal_status';
13
+ /** Where subagents are recorded, the agent's own as well as Moxxy's (`@moxxy/core` writes the same envelope). */
14
+ export const SUBAGENTS_PLUGIN_ID = asPluginId('@moxxy/subagents');
15
+
16
+ /** The agent session to resume for this provider: the last one recorded, if any. */
17
+ export function agentSessionFromEvents(events: ReadonlyArray<MoxxyEvent>, provider: string): string | undefined {
18
+ for (let i = events.length - 1; i >= 0; i -= 1) {
19
+ const event = events[i];
20
+ if (!event || event.type !== 'plugin_event') continue;
21
+ if (event.pluginId !== AGENT_CONNECTION_PLUGIN_ID || event.subtype !== AGENT_SESSION_SUBTYPE) continue;
22
+ const payload = event.payload as { provider?: unknown; sessionId?: unknown } | null;
23
+ if (payload && payload.provider === provider && typeof payload.sessionId === 'string') return payload.sessionId;
24
+ }
25
+ return undefined;
26
+ }
@@ -0,0 +1,102 @@
1
+ import { AGENT_CONNECTION_PLUGIN_ID, SUBAGENTS_PLUGIN_ID } from './agent-step.js';
2
+ import type { PluginId } from './ids.js';
3
+ import type { MoxxyEvent } from './events.js';
4
+ import type { AgentStepEvent } from './provider.js';
5
+
6
+ /** Everything the agent runs in the background now (`AGENT_CONNECTION_PLUGIN_ID`); each record replaces the one before. */
7
+ export const AGENT_BACKGROUND_WORK_SUBTYPE = 'background_work';
8
+ /** One background work ended (`AGENT_CONNECTION_PLUGIN_ID`), with what it printed last. */
9
+ export const AGENT_BACKGROUND_WORK_ENDED_SUBTYPE = 'background_work_ended';
10
+ /** What a subagent said as it worked (`SUBAGENTS_PLUGIN_ID`). */
11
+ export const SUBAGENT_MESSAGE_SUBTYPE = 'subagent_message';
12
+ /** How much of a command's output a `background_work_ended` keeps: its end, which says how it went. */
13
+ export const AGENT_WORK_OUTPUT_LIMIT = 4096;
14
+
15
+ /** A record of the agent's background work (its list, an end): read by the Agents pane, never drawn in the chat. */
16
+ export function isAgentWorkEvent(event: MoxxyEvent): boolean {
17
+ return (
18
+ event.type === 'plugin_event' &&
19
+ event.pluginId === AGENT_CONNECTION_PLUGIN_ID &&
20
+ (event.subtype === AGENT_BACKGROUND_WORK_SUBTYPE || event.subtype === AGENT_BACKGROUND_WORK_ENDED_SUBTYPE)
21
+ );
22
+ }
23
+
24
+ /** A record of a helper agent (its start, steps, words, end), from Moxxy's own subagents or an agent connection's. */
25
+ export function isSubagentEvent(event: MoxxyEvent): boolean {
26
+ return event.type === 'plugin_event' && event.pluginId === SUBAGENTS_PLUGIN_ID;
27
+ }
28
+
29
+ /** A `plugin_event` without its envelope (session, turn, id, time). */
30
+ export interface AgentActivityRecord {
31
+ readonly source: 'plugin' | 'system';
32
+ readonly pluginId: PluginId;
33
+ readonly subtype: string;
34
+ readonly payload: Record<string, unknown>;
35
+ }
36
+
37
+ /** The helpers of a conversation by id, kept to name them in the records of their later events. */
38
+ export type SubagentNames = Map<string, { readonly label: string; readonly agentType: string }>;
39
+
40
+ /**
41
+ * How an agent's helpers and background work are recorded in the log, the
42
+ * same whether they come in a turn or between turns; `undefined` for any
43
+ * other event.
44
+ */
45
+ export function agentActivityRecord(event: AgentStepEvent, provider: string, names: SubagentNames): AgentActivityRecord | undefined {
46
+ switch (event.type) {
47
+ case 'subagent_started':
48
+ names.set(event.id, { label: event.label, agentType: event.agentType });
49
+ return subagentRecord('subagent_started', {
50
+ childSessionId: event.id,
51
+ label: event.label,
52
+ agentType: event.agentType,
53
+ ...(event.prompt ? { prompt: event.prompt } : {}),
54
+ ...(event.toolsUnseen ? { toolsUnseen: true } : {}),
55
+ });
56
+ case 'subagent_tool_call': {
57
+ const named = names.get(event.id);
58
+ return subagentRecord('subagent_tool_call', { childSessionId: event.id, ...(named ? { label: named.label } : {}), name: event.name, input: event.input });
59
+ }
60
+ case 'subagent_message': {
61
+ const named = names.get(event.id);
62
+ return subagentRecord(SUBAGENT_MESSAGE_SUBTYPE, { childSessionId: event.id, ...(named ? { label: named.label } : {}), text: event.text });
63
+ }
64
+ case 'subagent_completed': {
65
+ const named = names.get(event.id);
66
+ return subagentRecord('subagent_completed', {
67
+ childSessionId: event.id,
68
+ ...(named ? { label: named.label, agentType: named.agentType } : {}),
69
+ text: event.text,
70
+ stopReason: event.stopped ? 'stopped' : event.error ? 'error' : 'end_turn',
71
+ ...(event.tokensUsed !== undefined ? { tokensUsed: event.tokensUsed } : {}),
72
+ ...(event.error ? { error: event.error } : {}),
73
+ });
74
+ }
75
+ case 'background_work':
76
+ return workRecord(AGENT_BACKGROUND_WORK_SUBTYPE, { provider, work: event.work.map(({ id, kind, label }) => ({ id, kind, label })) });
77
+ case 'background_work_ended':
78
+ return workRecord(AGENT_BACKGROUND_WORK_ENDED_SUBTYPE, {
79
+ provider,
80
+ id: event.id,
81
+ kind: event.kind,
82
+ label: event.label,
83
+ status: event.status,
84
+ ...(event.summary ? { summary: event.summary } : {}),
85
+ ...(event.output ? { output: outputEnd(event.output) } : {}),
86
+ });
87
+ default:
88
+ return undefined;
89
+ }
90
+ }
91
+
92
+ function subagentRecord(subtype: string, payload: Record<string, unknown>): AgentActivityRecord {
93
+ return { source: 'plugin', pluginId: SUBAGENTS_PLUGIN_ID, subtype, payload };
94
+ }
95
+
96
+ function workRecord(subtype: string, payload: Record<string, unknown>): AgentActivityRecord {
97
+ return { source: 'system', pluginId: AGENT_CONNECTION_PLUGIN_ID, subtype, payload };
98
+ }
99
+
100
+ function outputEnd(output: string): string {
101
+ return output.length <= AGENT_WORK_OUTPUT_LIMIT ? output : output.slice(-AGENT_WORK_OUTPUT_LIMIT);
102
+ }
@@ -2,7 +2,7 @@ import type { MoxxyEvent, UserPromptAttachment } from './events.js';
2
2
 
3
3
  /**
4
4
  * Attachment bytes travel two very different roads. The model needs them in
5
- * full; a client that only DRAWS the conversation (desktop, mobile, a channel
5
+ * full; a client that only DRAWS the conversation (desktop, a channel
6
6
  * bot) does not, and pushing a scanned 30 MB PDF through the client link on
7
7
  * every reconnect is what once wedged a chat in a reconnect loop. So the runner
8
8
  * sends clients {@link eventForClient} — large payloads swapped for their size —
@@ -1,11 +1,12 @@
1
1
  import { describe, expect, it } from 'vitest';
2
- import { AUTO_APPROVE_PLUGIN_ID, AUTO_APPROVE_SUBTYPE, autoApproveFromEvents, autoApproveSwitch } from './auto-approve.js';
2
+ import { AUTO_APPROVE_PLUGIN_ID, AUTO_APPROVE_SUBTYPE, autoApproveFromAgent, autoApproveFromEvents, autoApproveRecorded, autoApproveSwitch } from './auto-approve.js';
3
3
  import type { MoxxyEvent } from './events.js';
4
4
 
5
5
  let seq = 0;
6
6
  const base = () => ({ id: `e${seq}`, seq: seq++, ts: 0, sessionId: 's', turnId: 't' }) as const;
7
7
  const switched = (enabled: unknown): MoxxyEvent =>
8
8
  ({ ...base(), type: 'plugin_event', source: 'user', pluginId: AUTO_APPROVE_PLUGIN_ID, subtype: AUTO_APPROVE_SUBTYPE, payload: { enabled } }) as unknown as MoxxyEvent;
9
+ const fromSystem = (enabled: boolean): MoxxyEvent => ({ ...switched(enabled), source: 'system' }) as MoxxyEvent;
9
10
  const prompt = (): MoxxyEvent => ({ ...base(), type: 'user_prompt', source: 'user', text: 'hi' }) as unknown as MoxxyEvent;
10
11
  const otherPlugin = (): MoxxyEvent =>
11
12
  ({ ...base(), type: 'plugin_event', source: 'plugin', pluginId: 'x', subtype: AUTO_APPROVE_SUBTYPE, payload: { enabled: true } }) as unknown as MoxxyEvent;
@@ -26,4 +27,15 @@ describe('auto-approve fold', () => {
26
27
  expect(autoApproveSwitch(otherPlugin())).toBeNull();
27
28
  expect(autoApproveSwitch(switched('yes'))).toBeNull();
28
29
  });
30
+
31
+ it('says whether the conversation recorded a switch at all, even one back to off', () => {
32
+ expect(autoApproveRecorded([prompt(), otherPlugin()])).toBe(false);
33
+ expect(autoApproveRecorded([switched(true), prompt(), switched(false)])).toBe(true);
34
+ });
35
+
36
+ it("says whether the switch in force is the agent's own setup Moxxy took on, not the person's choice", () => {
37
+ expect(autoApproveFromAgent([prompt()])).toBe(false);
38
+ expect(autoApproveFromAgent([fromSystem(true), prompt()])).toBe(true);
39
+ expect(autoApproveFromAgent([fromSystem(true), switched(false), switched(true)])).toBe(false);
40
+ });
29
41
  });