@undefineds.co/models 0.2.0 → 0.2.1

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 (241) hide show
  1. package/README.md +515 -125
  2. package/dist/agent.repository.d.ts +20 -40
  3. package/dist/agent.schema.d.ts +20 -1
  4. package/dist/agent.schema.js +25 -1
  5. package/dist/ai-config/index.d.ts +8 -6
  6. package/dist/ai-config/index.js +41 -23
  7. package/dist/ai-model.schema.d.ts +13 -1
  8. package/dist/ai-model.schema.js +17 -1
  9. package/dist/ai-provider.schema.d.ts +9 -1
  10. package/dist/ai-provider.schema.js +13 -1
  11. package/dist/approval.schema.d.ts +21 -1
  12. package/dist/approval.schema.js +33 -1
  13. package/dist/audit.schema.d.ts +17 -1
  14. package/dist/audit.schema.js +29 -1
  15. package/dist/chat.repository.d.ts +19 -15
  16. package/dist/chat.schema.d.ts +37 -1
  17. package/dist/chat.schema.js +47 -1
  18. package/dist/client/index.d.ts +121 -0
  19. package/dist/client/index.js +299 -0
  20. package/dist/contact.repository.d.ts +21 -21
  21. package/dist/contact.schema.d.ts +48 -1
  22. package/dist/contact.schema.js +60 -1
  23. package/dist/credential.schema.d.ts +15 -1
  24. package/dist/credential.schema.js +19 -1
  25. package/dist/discovery/index.d.ts +2 -2
  26. package/dist/discovery/index.js +2 -2
  27. package/dist/extension/extension.schema.d.ts +4 -1
  28. package/dist/extension/extension.schema.js +11 -1
  29. package/dist/extension/index.d.ts +1 -1
  30. package/dist/favorite/favorite.schema.d.ts +24 -1
  31. package/dist/favorite/favorite.schema.js +34 -1
  32. package/dist/favorite/index.d.ts +2 -2
  33. package/dist/file/file.schema.d.ts +39 -1
  34. package/dist/file/file.schema.js +51 -1
  35. package/dist/file/index.d.ts +1 -1
  36. package/dist/fixtures/contracts-chat-contact.d.ts +8 -8
  37. package/dist/fixtures/contracts-chat-contact.js +5 -9
  38. package/dist/grant.schema.d.ts +16 -1
  39. package/dist/grant.schema.js +29 -1
  40. package/dist/import/index.d.ts +1 -1
  41. package/dist/inbox-notification.schema.d.ts +9 -1
  42. package/dist/inbox-notification.schema.js +16 -1
  43. package/dist/index.d.ts +42 -43
  44. package/dist/index.js +27 -26
  45. package/dist/knowledge/index.d.ts +1 -1
  46. package/dist/knowledge/knowledge-folder.schema.d.ts +7 -1
  47. package/dist/knowledge/knowledge-folder.schema.js +6 -1
  48. package/dist/message.repository.d.ts +20 -20
  49. package/dist/message.schema.d.ts +32 -1
  50. package/dist/message.schema.js +47 -1
  51. package/dist/namespaces.d.ts +178 -1
  52. package/dist/namespaces.js +253 -1
  53. package/dist/profile.d.ts +22 -1
  54. package/dist/profile.js +88 -1
  55. package/dist/profile.repository.d.ts +105 -0
  56. package/dist/profile.repository.js +47 -0
  57. package/dist/profile.schema.d.ts +16 -0
  58. package/dist/{core/profile.js → profile.schema.js} +4 -4
  59. package/dist/repository.d.ts +49 -9
  60. package/dist/repository.js +49 -46
  61. package/dist/schema.d.ts +244 -1
  62. package/dist/schema.js +37 -1
  63. package/dist/session/index.d.ts +1 -1
  64. package/dist/session/index.js +1 -1
  65. package/dist/session/session.schema.d.ts +43 -1
  66. package/dist/session/session.schema.js +46 -1
  67. package/dist/session.repository.d.ts +61 -0
  68. package/dist/session.repository.js +8 -0
  69. package/dist/settings/index.d.ts +1 -1
  70. package/dist/settings/settings.schema.d.ts +49 -1
  71. package/dist/settings/settings.schema.js +63 -1
  72. package/dist/{governance/runtime-projections.d.ts → sidecar/persistence-mapping.d.ts} +7 -7
  73. package/dist/{governance/runtime-projections.js → sidecar/persistence-mapping.js} +7 -1
  74. package/dist/{protocols/runtime-events.d.ts → sidecar/sidecar-events.d.ts} +35 -35
  75. package/dist/{protocols/runtime-events.js → sidecar/sidecar-events.js} +21 -4
  76. package/dist/thread.repository.d.ts +15 -19
  77. package/dist/thread.schema.d.ts +30 -1
  78. package/dist/thread.schema.js +39 -1
  79. package/dist/{core/types/collaboration-rich-content.d.ts → types/collaboration-blocks.d.ts} +7 -7
  80. package/dist/types/collaboration-blocks.js +7 -0
  81. package/dist/types/message-block.d.ts +270 -0
  82. package/dist/types/message-block.js +125 -0
  83. package/dist/vocab/_namespaces.d.ts +1 -1
  84. package/dist/vocab/index.d.ts +5 -6
  85. package/dist/vocab/index.js +0 -1
  86. package/dist/vocab/message.vocab.js +1 -1
  87. package/dist/vocab/thread.vocab.d.ts +1 -2
  88. package/dist/vocab/thread.vocab.js +2 -3
  89. package/dist/watch/index.d.ts +226 -0
  90. package/dist/watch/index.js +1156 -0
  91. package/package.json +41 -39
  92. package/dist/_drizzle-solid/columns.d.ts +0 -37
  93. package/dist/_drizzle-solid/columns.js +0 -1
  94. package/dist/_drizzle-solid/pod-table.d.ts +0 -3
  95. package/dist/_drizzle-solid/pod-table.js +0 -1
  96. package/dist/_drizzle-solid/types.d.ts +0 -40
  97. package/dist/_drizzle-solid/types.js +0 -1
  98. package/dist/ai-config.d.ts +0 -1
  99. package/dist/ai-config.js +0 -1
  100. package/dist/chat.utils.d.ts +0 -3
  101. package/dist/chat.utils.js +0 -30
  102. package/dist/core/agent-provider.schema.d.ts +0 -1
  103. package/dist/core/agent-provider.schema.js +0 -1
  104. package/dist/core/agent-status.schema.d.ts +0 -12
  105. package/dist/core/agent-status.schema.js +0 -18
  106. package/dist/core/agent.schema.d.ts +0 -72
  107. package/dist/core/agent.schema.js +0 -38
  108. package/dist/core/ai-config.schema.d.ts +0 -11
  109. package/dist/core/ai-config.schema.js +0 -18
  110. package/dist/core/ai-model.schema.d.ts +0 -15
  111. package/dist/core/ai-model.schema.js +0 -21
  112. package/dist/core/ai-provider.schema.d.ts +0 -16
  113. package/dist/core/ai-provider.schema.js +0 -28
  114. package/dist/core/approval.schema.d.ts +0 -21
  115. package/dist/core/approval.schema.js +0 -33
  116. package/dist/core/audit.schema.d.ts +0 -17
  117. package/dist/core/audit.schema.js +0 -29
  118. package/dist/core/chat.schema.d.ts +0 -36
  119. package/dist/core/chat.schema.js +0 -45
  120. package/dist/core/contact.schema.d.ts +0 -47
  121. package/dist/core/contact.schema.js +0 -59
  122. package/dist/core/credential.schema.d.ts +0 -56
  123. package/dist/core/credential.schema.js +0 -40
  124. package/dist/core/extension/extension.schema.d.ts +0 -4
  125. package/dist/core/extension/extension.schema.js +0 -11
  126. package/dist/core/extension/index.d.ts +0 -1
  127. package/dist/core/extension/index.js +0 -1
  128. package/dist/core/extension.d.ts +0 -1
  129. package/dist/core/extension.js +0 -1
  130. package/dist/core/favorite/favorite.schema.d.ts +0 -24
  131. package/dist/core/favorite/favorite.schema.js +0 -34
  132. package/dist/core/favorite/index.d.ts +0 -1
  133. package/dist/core/favorite/index.js +0 -1
  134. package/dist/core/file/file.schema.d.ts +0 -39
  135. package/dist/core/file/file.schema.js +0 -51
  136. package/dist/core/file/index.d.ts +0 -1
  137. package/dist/core/file/index.js +0 -1
  138. package/dist/core/file.d.ts +0 -1
  139. package/dist/core/file.js +0 -1
  140. package/dist/core/grant.schema.d.ts +0 -16
  141. package/dist/core/grant.schema.js +0 -29
  142. package/dist/core/import/import-job.schema.d.ts +0 -5
  143. package/dist/core/import/import-job.schema.js +0 -5
  144. package/dist/core/import/index.d.ts +0 -1
  145. package/dist/core/import/index.js +0 -1
  146. package/dist/core/import.d.ts +0 -1
  147. package/dist/core/import.js +0 -1
  148. package/dist/core/inbox-notification.schema.d.ts +0 -9
  149. package/dist/core/inbox-notification.schema.js +0 -16
  150. package/dist/core/index.d.ts +0 -31
  151. package/dist/core/index.js +0 -32
  152. package/dist/core/knowledge/index.d.ts +0 -1
  153. package/dist/core/knowledge/index.js +0 -1
  154. package/dist/core/knowledge/knowledge-folder.schema.d.ts +0 -7
  155. package/dist/core/knowledge/knowledge-folder.schema.js +0 -6
  156. package/dist/core/knowledge.d.ts +0 -1
  157. package/dist/core/knowledge.js +0 -1
  158. package/dist/core/message.schema.d.ts +0 -33
  159. package/dist/core/message.schema.js +0 -49
  160. package/dist/core/namespaces.d.ts +0 -176
  161. package/dist/core/namespaces.js +0 -289
  162. package/dist/core/profile.d.ts +0 -16
  163. package/dist/core/schema.d.ts +0 -304
  164. package/dist/core/schema.js +0 -46
  165. package/dist/core/session/index.d.ts +0 -1
  166. package/dist/core/session/index.js +0 -1
  167. package/dist/core/session/session.schema.d.ts +0 -6
  168. package/dist/core/session/session.schema.js +0 -6
  169. package/dist/core/session.d.ts +0 -1
  170. package/dist/core/session.js +0 -1
  171. package/dist/core/settings/index.d.ts +0 -1
  172. package/dist/core/settings/index.js +0 -1
  173. package/dist/core/settings/settings.schema.d.ts +0 -49
  174. package/dist/core/settings/settings.schema.js +0 -63
  175. package/dist/core/settings.d.ts +0 -1
  176. package/dist/core/settings.js +0 -1
  177. package/dist/core/thread.schema.d.ts +0 -28
  178. package/dist/core/thread.schema.js +0 -36
  179. package/dist/core/types/collaboration-rich-content.js +0 -7
  180. package/dist/core/types/message-rich-content.d.ts +0 -121
  181. package/dist/core/types/message-rich-content.js +0 -85
  182. package/dist/core/vector-store.schema.d.ts +0 -25
  183. package/dist/core/vector-store.schema.js +0 -35
  184. package/dist/core/vocab/_namespaces.d.ts +0 -1
  185. package/dist/core/vocab/_namespaces.js +0 -3
  186. package/dist/core/vocab/chat.vocab.d.ts +0 -16
  187. package/dist/core/vocab/chat.vocab.js +0 -22
  188. package/dist/core/vocab/contact.vocab.d.ts +0 -21
  189. package/dist/core/vocab/contact.vocab.js +0 -22
  190. package/dist/core/vocab/index.d.ts +0 -6
  191. package/dist/core/vocab/index.js +0 -6
  192. package/dist/core/vocab/message.vocab.d.ts +0 -20
  193. package/dist/core/vocab/message.vocab.js +0 -24
  194. package/dist/core/vocab/thread.vocab.d.ts +0 -11
  195. package/dist/core/vocab/thread.vocab.js +0 -14
  196. package/dist/core/vocab/workspace.vocab.d.ts +0 -12
  197. package/dist/core/vocab/workspace.vocab.js +0 -13
  198. package/dist/core/vocab.d.ts +0 -1
  199. package/dist/core/vocab.js +0 -1
  200. package/dist/core/workspace.schema.d.ts +0 -35
  201. package/dist/core/workspace.schema.js +0 -106
  202. package/dist/core.d.ts +0 -1
  203. package/dist/core.js +0 -1
  204. package/dist/discovery/vercel.d.ts +0 -25
  205. package/dist/discovery/vercel.js +0 -95
  206. package/dist/discovery.d.ts +0 -1
  207. package/dist/discovery.js +0 -1
  208. package/dist/extension.d.ts +0 -1
  209. package/dist/extension.js +0 -1
  210. package/dist/favorite.d.ts +0 -1
  211. package/dist/favorite.js +0 -1
  212. package/dist/file.d.ts +0 -1
  213. package/dist/file.js +0 -1
  214. package/dist/governance/index.d.ts +0 -1
  215. package/dist/governance/index.js +0 -1
  216. package/dist/import.d.ts +0 -1
  217. package/dist/import.js +0 -1
  218. package/dist/knowledge.d.ts +0 -1
  219. package/dist/knowledge.js +0 -1
  220. package/dist/protocols/index.d.ts +0 -1
  221. package/dist/protocols/index.js +0 -1
  222. package/dist/session.d.ts +0 -1
  223. package/dist/session.js +0 -1
  224. package/dist/settings.d.ts +0 -1
  225. package/dist/settings.js +0 -1
  226. package/dist/types/collaboration-rich-content.d.ts +0 -52
  227. package/dist/types/collaboration-rich-content.js +0 -7
  228. package/dist/types/message-rich-content.d.ts +0 -121
  229. package/dist/types/message-rich-content.js +0 -85
  230. package/dist/vocab/governance.vocab.d.ts +0 -46
  231. package/dist/vocab/governance.vocab.js +0 -47
  232. package/dist/vocab/workspace.vocab.d.ts +0 -12
  233. package/dist/vocab/workspace.vocab.js +0 -13
  234. package/dist/vocab.d.ts +0 -1
  235. package/dist/vocab.js +0 -1
  236. package/dist/workspace.repository.d.ts +0 -45
  237. package/dist/workspace.repository.js +0 -8
  238. package/dist/workspace.schema.d.ts +0 -1
  239. package/dist/workspace.schema.js +0 -1
  240. /package/dist/{core/vocab/governance.vocab.d.ts → vocab/sidecar.vocab.d.ts} +0 -0
  241. /package/dist/{core/vocab/governance.vocab.js → vocab/sidecar.vocab.js} +0 -0
@@ -0,0 +1,1156 @@
1
+ export const WATCH_HOME_DIRNAME = 'watch';
2
+ export const WATCH_SESSIONS_DIRNAME = 'sessions';
3
+ export const WATCH_SESSION_FILE_NAME = 'session.json';
4
+ export const WATCH_EVENTS_FILE_NAME = 'events.jsonl';
5
+ function fallbackRandomId() {
6
+ return Math.random().toString(36).slice(2, 10).padEnd(8, '0');
7
+ }
8
+ function extractWatchJsonText(value, depth = 0) {
9
+ if (depth > 4) {
10
+ return undefined;
11
+ }
12
+ if (typeof value === 'string') {
13
+ return value;
14
+ }
15
+ if (Array.isArray(value)) {
16
+ const parts = value
17
+ .map((item) => extractWatchJsonText(item, depth + 1))
18
+ .filter((item) => typeof item === 'string' && item.length > 0);
19
+ return parts.length > 0 ? parts.join('') : undefined;
20
+ }
21
+ if (!isRecord(value)) {
22
+ return undefined;
23
+ }
24
+ return firstNonEmpty([
25
+ extractWatchJsonText(value.text, depth + 1),
26
+ extractWatchJsonText(value.delta, depth + 1),
27
+ extractWatchJsonText(value.message, depth + 1),
28
+ extractWatchJsonText(value.content, depth + 1),
29
+ extractWatchJsonText(value.result, depth + 1),
30
+ extractWatchJsonText(value.summary, depth + 1),
31
+ extractWatchJsonText(value.error, depth + 1),
32
+ ]);
33
+ }
34
+ function extractWatchJsonArguments(value) {
35
+ if (isRecord(value)) {
36
+ return value;
37
+ }
38
+ if (typeof value !== 'string') {
39
+ return undefined;
40
+ }
41
+ try {
42
+ const parsed = JSON.parse(value);
43
+ return isRecord(parsed) ? parsed : undefined;
44
+ }
45
+ catch {
46
+ return undefined;
47
+ }
48
+ }
49
+ function normalizeWatchUserInputOption(value) {
50
+ if (!isRecord(value)) {
51
+ return null;
52
+ }
53
+ const label = firstNonEmpty([
54
+ typeof value.label === 'string' ? value.label : undefined,
55
+ typeof value.value === 'string' ? value.value : undefined,
56
+ ]);
57
+ if (!label) {
58
+ return null;
59
+ }
60
+ const description = firstNonEmpty([
61
+ typeof value.description === 'string' ? value.description : undefined,
62
+ typeof value.details === 'string' ? value.details : undefined,
63
+ ]);
64
+ return {
65
+ label,
66
+ ...(description ? { description } : {}),
67
+ };
68
+ }
69
+ export function normalizeWatchUserInputQuestion(value, fallbackId = 'question-1') {
70
+ if (!isRecord(value)) {
71
+ return null;
72
+ }
73
+ const header = firstNonEmpty([
74
+ typeof value.header === 'string' ? value.header : undefined,
75
+ typeof value.title === 'string' ? value.title : undefined,
76
+ typeof value.label === 'string' ? value.label : undefined,
77
+ ]) ?? 'Question';
78
+ const question = firstNonEmpty([
79
+ typeof value.question === 'string' ? value.question : undefined,
80
+ typeof value.prompt === 'string' ? value.prompt : undefined,
81
+ typeof value.message === 'string' ? value.message : undefined,
82
+ header,
83
+ ]) ?? header;
84
+ const options = Array.isArray(value.options)
85
+ ? value.options
86
+ .map((option) => normalizeWatchUserInputOption(option))
87
+ .filter((option) => option !== null)
88
+ : [];
89
+ return {
90
+ id: firstNonEmpty([typeof value.id === 'string' ? value.id : undefined, fallbackId]) ?? fallbackId,
91
+ header,
92
+ question,
93
+ options,
94
+ };
95
+ }
96
+ export function resolveWatchQuestionAnswer(question, answer) {
97
+ const normalized = answer.trim();
98
+ if (!normalized) {
99
+ return [];
100
+ }
101
+ if (question.options.length > 0 && /^\d+$/u.test(normalized)) {
102
+ const index = Number(normalized) - 1;
103
+ const option = question.options[index];
104
+ if (option?.label) {
105
+ return [option.label];
106
+ }
107
+ }
108
+ return [normalized];
109
+ }
110
+ function recordFromUnknown(value) {
111
+ return isRecord(value) ? value : null;
112
+ }
113
+ function extractAcpCommand(value) {
114
+ if (typeof value === 'string' && value.trim()) {
115
+ return value.trim();
116
+ }
117
+ const record = recordFromUnknown(value);
118
+ if (!record) {
119
+ return undefined;
120
+ }
121
+ const command = firstNonEmpty([
122
+ typeof record.command === 'string' ? record.command : undefined,
123
+ typeof record.cmd === 'string' ? record.cmd : undefined,
124
+ ]);
125
+ const args = Array.isArray(record.args)
126
+ ? record.args.filter((item) => typeof item === 'string' && item.trim().length > 0)
127
+ : [];
128
+ if (command && args.length > 0) {
129
+ return `${command} ${args.join(' ')}`;
130
+ }
131
+ return command ?? extractWatchJsonText(record);
132
+ }
133
+ function normalizeAcpPermissionOptions(value) {
134
+ if (!Array.isArray(value)) {
135
+ return [];
136
+ }
137
+ return value.filter((item) => isRecord(item));
138
+ }
139
+ function selectAcpPermissionOption(options, decision) {
140
+ if (decision === 'cancel') {
141
+ return undefined;
142
+ }
143
+ const preferredKinds = decision === 'accept'
144
+ ? ['allow_once', 'allow_always']
145
+ : decision === 'accept_for_session'
146
+ ? ['allow_always', 'allow_once']
147
+ : ['reject_once', 'reject_always'];
148
+ for (const kind of preferredKinds) {
149
+ const match = options.find((option) => option.kind === kind && typeof option.optionId === 'string');
150
+ if (match && typeof match.optionId === 'string') {
151
+ return match.optionId;
152
+ }
153
+ }
154
+ const preferredNames = decision === 'decline'
155
+ ? ['reject', 'deny', 'decline', 'no']
156
+ : ['allow', 'approve', 'yes'];
157
+ for (const option of options) {
158
+ if (typeof option.optionId !== 'string' || typeof option.name !== 'string') {
159
+ continue;
160
+ }
161
+ const name = option.name.toLowerCase();
162
+ if (preferredNames.some((token) => name.includes(token))) {
163
+ return option.optionId;
164
+ }
165
+ }
166
+ const fallback = decision === 'decline'
167
+ ? options.find((option) => typeof option.optionId === 'string')
168
+ : options.find((option) => typeof option.optionId === 'string');
169
+ return typeof fallback?.optionId === 'string' ? fallback.optionId : undefined;
170
+ }
171
+ export function createWatchSessionId(options = {}) {
172
+ const now = options.now ?? new Date();
173
+ const randomId = (options.randomId?.trim() || globalThis.crypto?.randomUUID?.() || fallbackRandomId()).slice(0, 8);
174
+ const stamp = now.toISOString().replace(/[:.]/g, '-');
175
+ return `watch_${stamp}_${randomId}`;
176
+ }
177
+ export function normalizeWatchCredentialSource(source) {
178
+ return source ?? 'auto';
179
+ }
180
+ export function shouldAttemptCloudCredentialProbe(requestedSource, localAuthStatus) {
181
+ return requestedSource === 'cloud'
182
+ || (requestedSource === 'auto' && localAuthStatus.state === 'unauthenticated');
183
+ }
184
+ export function formatWatchAutoFallbackMessage(localMessage, detail) {
185
+ return `${localMessage} Cloud credential fallback unavailable: ${detail}`;
186
+ }
187
+ export function resolveWatchCredentialSourceResolution(input) {
188
+ const requestedSource = normalizeWatchCredentialSource(input.requestedSource);
189
+ const localAuthStatus = input.localAuthStatus;
190
+ const cloudCredentialProbe = input.cloudCredentialProbe;
191
+ if (requestedSource === 'local') {
192
+ return {
193
+ requestedSource,
194
+ resolvedSource: 'local',
195
+ authStatus: localAuthStatus,
196
+ };
197
+ }
198
+ if (requestedSource === 'cloud') {
199
+ if (cloudCredentialProbe?.status === 'available') {
200
+ return {
201
+ requestedSource,
202
+ resolvedSource: 'cloud',
203
+ authStatus: { state: 'authenticated' },
204
+ };
205
+ }
206
+ return {
207
+ requestedSource,
208
+ authStatus: { state: 'unauthenticated', message: cloudCredentialProbe?.message },
209
+ error: cloudCredentialProbe?.message ?? 'Cloud credential resolution unavailable.',
210
+ };
211
+ }
212
+ if (localAuthStatus.state !== 'unauthenticated') {
213
+ return {
214
+ requestedSource,
215
+ resolvedSource: 'local',
216
+ authStatus: localAuthStatus,
217
+ };
218
+ }
219
+ if (cloudCredentialProbe?.status === 'available') {
220
+ return {
221
+ requestedSource,
222
+ resolvedSource: 'cloud',
223
+ authStatus: { state: 'authenticated' },
224
+ };
225
+ }
226
+ const localMessage = localAuthStatus.message ?? input.defaultLocalMessage ?? 'Local authentication unavailable.';
227
+ const detail = cloudCredentialProbe?.message ?? 'Cloud credential fallback unavailable.';
228
+ return {
229
+ requestedSource,
230
+ resolvedSource: 'local',
231
+ authStatus: {
232
+ state: 'unauthenticated',
233
+ message: formatWatchAutoFallbackMessage(localMessage, detail),
234
+ },
235
+ };
236
+ }
237
+ export function resolveWatchAutoApprovalDecision(input) {
238
+ const { mode, request } = input;
239
+ if (request.kind === 'command-approval') {
240
+ if (mode === 'auto') {
241
+ return 'accept_for_session';
242
+ }
243
+ if (mode === 'smart' && isTrustedWatchCommand(request.command)) {
244
+ return 'accept';
245
+ }
246
+ return null;
247
+ }
248
+ if (request.kind === 'file-change-approval') {
249
+ if (mode === 'auto') {
250
+ return 'accept_for_session';
251
+ }
252
+ if (mode === 'smart') {
253
+ return 'accept';
254
+ }
255
+ return null;
256
+ }
257
+ if (request.kind === 'permissions-approval') {
258
+ if (mode === 'auto') {
259
+ return 'accept_for_session';
260
+ }
261
+ return null;
262
+ }
263
+ return null;
264
+ }
265
+ function isRecord(value) {
266
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
267
+ }
268
+ function firstNonEmpty(values) {
269
+ for (const value of values) {
270
+ if (typeof value === 'string' && value.trim()) {
271
+ return value.trim();
272
+ }
273
+ }
274
+ return undefined;
275
+ }
276
+ function normalizeWatchAuthText(value, depth = 0) {
277
+ if (depth > 5) {
278
+ return undefined;
279
+ }
280
+ if (typeof value === 'string') {
281
+ return value;
282
+ }
283
+ if (Array.isArray(value)) {
284
+ const parts = value
285
+ .map((item) => normalizeWatchAuthText(item, depth + 1))
286
+ .filter((item) => typeof item === 'string' && item.trim().length > 0);
287
+ return parts.length > 0 ? parts.join(' ') : undefined;
288
+ }
289
+ if (!isRecord(value)) {
290
+ return undefined;
291
+ }
292
+ return firstNonEmpty([
293
+ normalizeWatchAuthText(value.message, depth + 1),
294
+ normalizeWatchAuthText(value.error, depth + 1),
295
+ normalizeWatchAuthText(value.result, depth + 1),
296
+ normalizeWatchAuthText(value.text, depth + 1),
297
+ normalizeWatchAuthText(value.reason, depth + 1),
298
+ normalizeWatchAuthText(value.content, depth + 1),
299
+ normalizeWatchAuthText(value.summary, depth + 1),
300
+ ]);
301
+ }
302
+ export function getWatchAuthLoginCommand(backend) {
303
+ if (backend === 'claude') {
304
+ return 'claude auth login';
305
+ }
306
+ if (backend === 'codex') {
307
+ return 'codex login';
308
+ }
309
+ return null;
310
+ }
311
+ export function formatWatchBackendAuthMessage(backend, detail) {
312
+ const command = getWatchAuthLoginCommand(backend);
313
+ const label = backend === 'claude'
314
+ ? 'Claude Code'
315
+ : backend === 'codebuddy'
316
+ ? 'CodeBuddy Code'
317
+ : 'Codex';
318
+ if (backend === 'codebuddy') {
319
+ return detail
320
+ ? `${label} is not authenticated. Open \`codebuddy\` and complete login first. Native message: ${detail}`
321
+ : `${label} is not authenticated. Open \`codebuddy\` and complete login first.`;
322
+ }
323
+ return detail
324
+ ? `${label} is not authenticated. Run \`${command}\` and try again. Native message: ${detail}`
325
+ : `${label} is not authenticated. Run \`${command}\` and try again.`;
326
+ }
327
+ export function looksLikeWatchAuthFailureText(text) {
328
+ return [
329
+ /\bnot logged in\b/iu,
330
+ /\bauthentication_failed\b/iu,
331
+ /\bunauthenticated\b/iu,
332
+ /\bauthentication required\b/iu,
333
+ /\bplease run \/login\b/iu,
334
+ /\bplease sign in\b/iu,
335
+ /\bsign in first\b/iu,
336
+ /\blogin required\b/iu,
337
+ /\bunauthorized\b/iu,
338
+ /\binvalid api key\b/iu,
339
+ ].some((pattern) => pattern.test(text));
340
+ }
341
+ export function parseWatchClaudeAuthStatus(stdout) {
342
+ const payload = parseWatchJsonLine(stdout.trim());
343
+ if (!payload || typeof payload.loggedIn !== 'boolean') {
344
+ return { state: 'unknown' };
345
+ }
346
+ return payload.loggedIn
347
+ ? { state: 'authenticated' }
348
+ : { state: 'unauthenticated', message: formatWatchBackendAuthMessage('claude') };
349
+ }
350
+ export function detectWatchAuthFailure(backend, line) {
351
+ const trimmed = line.trim();
352
+ if (!trimmed) {
353
+ return null;
354
+ }
355
+ const payload = parseWatchJsonLine(trimmed);
356
+ if (payload) {
357
+ if (backend === 'claude' && payload.error === 'authentication_failed') {
358
+ const detail = normalizeWatchAuthText(payload) ?? 'authentication_failed';
359
+ return { message: formatWatchBackendAuthMessage(backend, detail) };
360
+ }
361
+ if (backend === 'claude' && payload.loggedIn === false) {
362
+ return { message: formatWatchBackendAuthMessage(backend) };
363
+ }
364
+ if (payload.error) {
365
+ const detail = normalizeWatchAuthText(payload.error) ?? normalizeWatchAuthText(payload);
366
+ if (detail && looksLikeWatchAuthFailureText(detail)) {
367
+ return { message: formatWatchBackendAuthMessage(backend, detail) };
368
+ }
369
+ }
370
+ if (payload.is_error === true) {
371
+ const detail = normalizeWatchAuthText(payload);
372
+ if (detail && looksLikeWatchAuthFailureText(detail)) {
373
+ return { message: formatWatchBackendAuthMessage(backend, detail) };
374
+ }
375
+ }
376
+ const detail = normalizeWatchAuthText(payload);
377
+ if (detail && looksLikeWatchAuthFailureText(detail)) {
378
+ return { message: formatWatchBackendAuthMessage(backend, detail) };
379
+ }
380
+ }
381
+ if (!looksLikeWatchAuthFailureText(trimmed)) {
382
+ return null;
383
+ }
384
+ return { message: formatWatchBackendAuthMessage(backend, trimmed) };
385
+ }
386
+ export function parseWatchJsonLine(line) {
387
+ try {
388
+ const parsed = JSON.parse(line);
389
+ return isRecord(parsed) ? parsed : null;
390
+ }
391
+ catch {
392
+ return null;
393
+ }
394
+ }
395
+ export function extractWatchSessionIdFromJsonLine(line) {
396
+ const json = parseWatchJsonLine(line);
397
+ if (!json) {
398
+ return undefined;
399
+ }
400
+ return firstNonEmpty([
401
+ typeof json.session_id === 'string' ? json.session_id : undefined,
402
+ typeof json.sessionId === 'string' ? json.sessionId : undefined,
403
+ isRecord(json.message) && typeof json.message.session_id === 'string' ? json.message.session_id : undefined,
404
+ isRecord(json.message) && typeof json.message.sessionId === 'string' ? json.message.sessionId : undefined,
405
+ ]);
406
+ }
407
+ export function isTrustedWatchCommand(command) {
408
+ if (!command) {
409
+ return false;
410
+ }
411
+ const normalized = command.trim();
412
+ const safePatterns = [
413
+ /^pwd(?:\s|$)/,
414
+ /^ls(?:\s|$)/,
415
+ /^cat(?:\s|$)/,
416
+ /^sed(?:\s|$)/,
417
+ /^head(?:\s|$)/,
418
+ /^tail(?:\s|$)/,
419
+ /^wc(?:\s|$)/,
420
+ /^sort(?:\s|$)/,
421
+ /^uniq(?:\s|$)/,
422
+ /^find(?:\s|$)/,
423
+ /^grep(?:\s|$)/,
424
+ /^rg(?:\s|$)/,
425
+ /^git status(?:\s|$)/,
426
+ /^git diff(?:\s|$)/,
427
+ /^git log(?:\s|$)/,
428
+ ];
429
+ return safePatterns.some((pattern) => pattern.test(normalized));
430
+ }
431
+ export function normalizeCodexAppServerInteractionRequest(message) {
432
+ const method = typeof message.method === 'string' ? message.method : '';
433
+ const params = (typeof message.params === 'object' && message.params !== null
434
+ ? message.params
435
+ : {});
436
+ if (method === 'item/commandExecution/requestApproval') {
437
+ const command = typeof params.command === 'string' ? params.command : undefined;
438
+ const cwd = typeof params.cwd === 'string' ? params.cwd : undefined;
439
+ return {
440
+ kind: 'command-approval',
441
+ message: command || 'Codex requests command approval',
442
+ command,
443
+ cwd,
444
+ raw: message,
445
+ };
446
+ }
447
+ if (method === 'item/fileChange/requestApproval') {
448
+ const reason = typeof params.reason === 'string' ? params.reason : undefined;
449
+ return {
450
+ kind: 'file-change-approval',
451
+ message: reason && reason.trim() ? reason : 'Codex requests file-change approval',
452
+ ...(reason ? { reason } : {}),
453
+ raw: message,
454
+ };
455
+ }
456
+ if (method === 'item/permissions/requestApproval') {
457
+ const reason = typeof params.reason === 'string' ? params.reason : undefined;
458
+ const permissions = isRecord(params.permissions) ? params.permissions : {};
459
+ return {
460
+ kind: 'permissions-approval',
461
+ message: reason && reason.trim() ? reason : 'Codex requests additional permissions',
462
+ permissions,
463
+ raw: message,
464
+ };
465
+ }
466
+ if (method === 'item/tool/requestUserInput') {
467
+ const questions = Array.isArray(params.questions)
468
+ ? params.questions
469
+ .map((question, index) => normalizeWatchUserInputQuestion(question, `question-${index + 1}`))
470
+ .filter((question) => question !== null)
471
+ : [];
472
+ return {
473
+ kind: 'user-input',
474
+ message: 'Codex requests structured user input',
475
+ questions,
476
+ raw: message,
477
+ };
478
+ }
479
+ if (method === 'applyPatchApproval' || method === 'execCommandApproval') {
480
+ return {
481
+ kind: 'codex-approval',
482
+ message: 'Codex requests approval',
483
+ raw: message,
484
+ };
485
+ }
486
+ return null;
487
+ }
488
+ export function resolveWatchInteractionAutoResponse(input) {
489
+ const { mode, request } = input;
490
+ if (request.kind === 'user-input' || request.kind === 'codex-approval') {
491
+ return null;
492
+ }
493
+ const decision = resolveWatchAutoApprovalDecision({
494
+ mode,
495
+ request,
496
+ });
497
+ if (!decision) {
498
+ return null;
499
+ }
500
+ return buildCodexApprovalResponse(request, decision);
501
+ }
502
+ export function buildCodexApprovalResponse(request, decision) {
503
+ if (request.kind === 'permissions-approval') {
504
+ if (decision === 'accept') {
505
+ return { permissions: request.permissions, scope: 'turn' };
506
+ }
507
+ if (decision === 'accept_for_session') {
508
+ return { permissions: request.permissions, scope: 'session' };
509
+ }
510
+ return { permissions: {}, scope: 'turn' };
511
+ }
512
+ if (request.kind === 'codex-approval') {
513
+ if (decision === 'accept') {
514
+ return { decision: 'approved' };
515
+ }
516
+ if (decision === 'accept_for_session') {
517
+ return { decision: 'approved_for_session' };
518
+ }
519
+ if (decision === 'cancel') {
520
+ return { decision: 'abort' };
521
+ }
522
+ return { decision: 'denied' };
523
+ }
524
+ if (decision === 'accept') {
525
+ return { decision: 'accept' };
526
+ }
527
+ if (decision === 'accept_for_session') {
528
+ return { decision: 'acceptForSession' };
529
+ }
530
+ if (decision === 'cancel') {
531
+ return { decision: 'cancel' };
532
+ }
533
+ return { decision: 'decline' };
534
+ }
535
+ export function buildCodexUserInputResponse(answers) {
536
+ return { answers };
537
+ }
538
+ export function buildWatchUserInputResponse(answers) {
539
+ return buildCodexUserInputResponse(answers);
540
+ }
541
+ export function normalizeAcpInteractionRequest(message) {
542
+ const method = typeof message.method === 'string' ? message.method.toLowerCase() : '';
543
+ const params = (recordFromUnknown(message.params) ?? {});
544
+ if (method === 'session/request_permission' || Array.isArray(params.options)) {
545
+ const toolCall = (recordFromUnknown(params.toolCall) ?? {});
546
+ const toolKind = typeof toolCall.kind === 'string' ? toolCall.kind : '';
547
+ const command = extractAcpCommand(toolCall.rawInput);
548
+ const cwd = firstNonEmpty([
549
+ typeof toolCall.cwd === 'string' ? toolCall.cwd : undefined,
550
+ recordFromUnknown(toolCall.rawInput) && typeof recordFromUnknown(toolCall.rawInput)?.cwd === 'string'
551
+ ? recordFromUnknown(toolCall.rawInput)?.cwd
552
+ : undefined,
553
+ ]);
554
+ const messageText = firstNonEmpty([
555
+ typeof toolCall.title === 'string' ? toolCall.title : undefined,
556
+ command,
557
+ extractWatchJsonText(toolCall),
558
+ method || undefined,
559
+ ]) ?? 'Approval required';
560
+ if (toolKind === 'execute' || command) {
561
+ return {
562
+ kind: 'command-approval',
563
+ message: command ?? messageText,
564
+ ...(command ? { command } : {}),
565
+ ...(cwd ? { cwd } : {}),
566
+ raw: message,
567
+ };
568
+ }
569
+ if (toolKind === 'edit' || toolKind === 'delete' || toolKind === 'move') {
570
+ return {
571
+ kind: 'file-change-approval',
572
+ message: messageText,
573
+ reason: messageText,
574
+ raw: message,
575
+ };
576
+ }
577
+ return {
578
+ kind: 'permissions-approval',
579
+ message: messageText,
580
+ permissions: recordFromUnknown(toolCall.rawInput) ?? {},
581
+ raw: message,
582
+ };
583
+ }
584
+ const looksLikeInput = method.includes('request_input')
585
+ || method.includes('requestuserinput')
586
+ || method.includes('user_input')
587
+ || Array.isArray(params.questions);
588
+ if (!looksLikeInput) {
589
+ return null;
590
+ }
591
+ const questions = Array.isArray(params.questions)
592
+ ? params.questions
593
+ .map((question, index) => normalizeWatchUserInputQuestion(question, `question-${index + 1}`))
594
+ .filter((question) => question !== null)
595
+ : [];
596
+ return {
597
+ kind: 'user-input',
598
+ message: firstNonEmpty([
599
+ extractWatchJsonText(params.message),
600
+ extractWatchJsonText(params.prompt),
601
+ extractWatchJsonText(params.question),
602
+ questions[0]?.question,
603
+ ]) ?? 'Input required',
604
+ questions,
605
+ raw: message,
606
+ };
607
+ }
608
+ export function normalizeAcpRequest(message) {
609
+ const interaction = normalizeAcpInteractionRequest(message);
610
+ if (!interaction) {
611
+ return [];
612
+ }
613
+ if (interaction.kind === 'user-input') {
614
+ return [{
615
+ type: 'input.required',
616
+ message: interaction.message,
617
+ request: interaction,
618
+ raw: message,
619
+ }];
620
+ }
621
+ const events = [{
622
+ type: 'approval.required',
623
+ message: interaction.message,
624
+ request: interaction,
625
+ raw: message,
626
+ }];
627
+ if (interaction.kind === 'command-approval' && interaction.command) {
628
+ events.push({
629
+ type: 'tool.call',
630
+ name: 'commandExecution',
631
+ arguments: {
632
+ command: interaction.command,
633
+ cwd: interaction.cwd,
634
+ },
635
+ raw: message,
636
+ });
637
+ }
638
+ return events;
639
+ }
640
+ function normalizeAcpToolCallEvent(update, raw) {
641
+ const name = firstNonEmpty([
642
+ typeof update.title === 'string' ? update.title : undefined,
643
+ typeof update.kind === 'string' ? update.kind : undefined,
644
+ typeof update.toolCallId === 'string' ? update.toolCallId : undefined,
645
+ ]);
646
+ if (!name) {
647
+ return null;
648
+ }
649
+ return {
650
+ type: 'tool.call',
651
+ name,
652
+ arguments: extractWatchJsonArguments(update.rawInput),
653
+ raw,
654
+ };
655
+ }
656
+ export function normalizeAcpSessionNotification(message) {
657
+ const method = typeof message.method === 'string' ? message.method : '';
658
+ const params = (recordFromUnknown(message.params) ?? {});
659
+ if (method !== 'session/update') {
660
+ return [];
661
+ }
662
+ const update = (recordFromUnknown(params.update) ?? {});
663
+ const updateType = firstNonEmpty([
664
+ typeof update.sessionUpdate === 'string' ? update.sessionUpdate : undefined,
665
+ typeof update.type === 'string' ? update.type : undefined,
666
+ ])?.toLowerCase() ?? '';
667
+ if (updateType === 'available_commands_update' || updateType === 'usage_update') {
668
+ return [];
669
+ }
670
+ if (updateType === 'agent_message_chunk') {
671
+ const text = extractWatchJsonText(update.content ?? update);
672
+ return text ? [{ type: 'assistant.delta', text, raw: message }] : [];
673
+ }
674
+ if (updateType === 'agent_thought_chunk') {
675
+ return [];
676
+ }
677
+ if (updateType === 'tool_call' || updateType === 'tool_call_update') {
678
+ const toolEvent = normalizeAcpToolCallEvent(update, message);
679
+ if (toolEvent) {
680
+ return [toolEvent];
681
+ }
682
+ }
683
+ const text = extractWatchJsonText(update);
684
+ if (!text) {
685
+ return [];
686
+ }
687
+ return [{
688
+ type: 'session.note',
689
+ message: text,
690
+ raw: message,
691
+ }];
692
+ }
693
+ export function buildAcpPermissionResponse(request, decision) {
694
+ if (decision === 'cancel') {
695
+ return {
696
+ outcome: { outcome: 'cancelled' },
697
+ };
698
+ }
699
+ const raw = recordFromUnknown(request.raw);
700
+ const params = recordFromUnknown(raw?.params) ?? {};
701
+ const optionId = selectAcpPermissionOption(normalizeAcpPermissionOptions(params.options), decision);
702
+ if (!optionId) {
703
+ return {
704
+ outcome: { outcome: 'cancelled' },
705
+ };
706
+ }
707
+ return {
708
+ outcome: {
709
+ outcome: 'selected',
710
+ optionId,
711
+ },
712
+ };
713
+ }
714
+ function maybeWatchToolEvent(json, lowerType) {
715
+ const toolName = firstNonEmpty([
716
+ typeof json.toolName === 'string' ? json.toolName : undefined,
717
+ typeof json.name === 'string' ? json.name : undefined,
718
+ typeof json.tool === 'string' ? json.tool : undefined,
719
+ isRecord(json.tool) && typeof json.tool.name === 'string' ? json.tool.name : undefined,
720
+ typeof json.command === 'string' ? json.command : undefined,
721
+ ]);
722
+ const looksLikeTool = lowerType.includes('tool')
723
+ || lowerType.includes('command')
724
+ || lowerType.includes('function_call')
725
+ || (toolName !== undefined && !lowerType.includes('approval'));
726
+ if (!looksLikeTool || !toolName) {
727
+ return null;
728
+ }
729
+ return {
730
+ type: 'tool.call',
731
+ name: toolName,
732
+ arguments: extractWatchJsonArguments(json.arguments ?? json.args ?? json.input),
733
+ raw: json,
734
+ };
735
+ }
736
+ function maybeWatchInputEvent(json, lowerType) {
737
+ const looksLikeInput = lowerType.includes('request_user_input')
738
+ || lowerType.includes('user_input')
739
+ || Array.isArray(json.questions);
740
+ if (!looksLikeInput) {
741
+ return null;
742
+ }
743
+ const questions = Array.isArray(json.questions)
744
+ ? json.questions
745
+ .map((question, index) => normalizeWatchUserInputQuestion(question, `question-${index + 1}`))
746
+ .filter((question) => question !== null)
747
+ : [];
748
+ const message = firstNonEmpty([
749
+ extractWatchJsonText(json.message),
750
+ extractWatchJsonText(json.prompt),
751
+ extractWatchJsonText(json.question),
752
+ extractWatchJsonText(json.description),
753
+ ]) || 'Input required';
754
+ const request = {
755
+ kind: 'user-input',
756
+ message,
757
+ questions,
758
+ raw: json,
759
+ };
760
+ return {
761
+ type: 'input.required',
762
+ message,
763
+ request,
764
+ raw: json,
765
+ };
766
+ }
767
+ function maybeWatchApprovalEvent(json, lowerType) {
768
+ const looksLikeApproval = lowerType.includes('approval')
769
+ || lowerType.includes('permission')
770
+ || isRecord(json.permissions);
771
+ if (!looksLikeApproval) {
772
+ return null;
773
+ }
774
+ const message = firstNonEmpty([
775
+ extractWatchJsonText(json.message),
776
+ extractWatchJsonText(json.reason),
777
+ extractWatchJsonText(json.prompt),
778
+ extractWatchJsonText(json.question),
779
+ extractWatchJsonText(json.description),
780
+ lowerType || undefined,
781
+ ]);
782
+ return {
783
+ type: 'approval.required',
784
+ message: message || 'Approval required',
785
+ ...(isRecord(json.permissions)
786
+ ? {
787
+ request: {
788
+ kind: 'permissions-approval',
789
+ message: message || 'Approval required',
790
+ permissions: json.permissions,
791
+ raw: json,
792
+ },
793
+ }
794
+ : {}),
795
+ raw: json,
796
+ };
797
+ }
798
+ function maybeWatchAssistantDoneEvent(json, lowerType) {
799
+ const isDone = lowerType.includes('done')
800
+ || lowerType.includes('completed')
801
+ || lowerType === 'result'
802
+ || lowerType.endsWith('.result')
803
+ || lowerType.endsWith('.done');
804
+ if (!isDone) {
805
+ return null;
806
+ }
807
+ return {
808
+ type: 'assistant.done',
809
+ text: extractWatchJsonText(json),
810
+ raw: json,
811
+ };
812
+ }
813
+ function maybeWatchAssistantDeltaEvent(json, lowerType) {
814
+ const isDelta = lowerType.includes('delta')
815
+ || lowerType.includes('assistant')
816
+ || lowerType.includes('message')
817
+ || lowerType.includes('content_block')
818
+ || lowerType.includes('text');
819
+ if (!isDelta) {
820
+ return null;
821
+ }
822
+ const text = firstNonEmpty([
823
+ extractWatchJsonText(json.delta),
824
+ extractWatchJsonText(json.text),
825
+ extractWatchJsonText(json.message),
826
+ extractWatchJsonText(json.content),
827
+ ]);
828
+ if (!text) {
829
+ return null;
830
+ }
831
+ return {
832
+ type: 'assistant.delta',
833
+ text,
834
+ raw: json,
835
+ };
836
+ }
837
+ export function parseWatchJsonProtocolLine(line) {
838
+ const json = parseWatchJsonLine(line);
839
+ if (!json) {
840
+ return [];
841
+ }
842
+ const lowerType = typeof json.type === 'string' ? json.type.toLowerCase() : '';
843
+ const events = [];
844
+ const inputEvent = maybeWatchInputEvent(json, lowerType);
845
+ if (inputEvent) {
846
+ events.push(inputEvent);
847
+ }
848
+ const approvalEvent = maybeWatchApprovalEvent(json, lowerType);
849
+ if (approvalEvent) {
850
+ events.push(approvalEvent);
851
+ }
852
+ const toolEvent = maybeWatchToolEvent(json, lowerType);
853
+ if (toolEvent) {
854
+ events.push(toolEvent);
855
+ }
856
+ const doneEvent = maybeWatchAssistantDoneEvent(json, lowerType);
857
+ if (doneEvent) {
858
+ events.push(doneEvent);
859
+ }
860
+ else {
861
+ const deltaEvent = maybeWatchAssistantDeltaEvent(json, lowerType);
862
+ if (deltaEvent) {
863
+ events.push(deltaEvent);
864
+ }
865
+ }
866
+ if (events.length === 0) {
867
+ const text = extractWatchJsonText(json);
868
+ if (text) {
869
+ events.push({
870
+ type: 'session.note',
871
+ message: text,
872
+ raw: json,
873
+ });
874
+ }
875
+ }
876
+ return events;
877
+ }
878
+ function normalizeCodexThreadItem(item, raw) {
879
+ const type = typeof item.type === 'string' ? item.type : '';
880
+ if (type === 'userMessage') {
881
+ const content = Array.isArray(item.content)
882
+ ? item.content
883
+ .map((part) => (typeof part === 'object' && part !== null ? part : null))
884
+ .filter((part) => part !== null)
885
+ .map((part) => (typeof part.text === 'string' ? part.text : ''))
886
+ .filter((text) => text.length > 0)
887
+ .join('')
888
+ : '';
889
+ return content
890
+ ? [{
891
+ type: 'session.note',
892
+ message: `userMessage · ${content}`,
893
+ raw,
894
+ }]
895
+ : [];
896
+ }
897
+ if (type === 'commandExecution') {
898
+ return [{
899
+ type: 'tool.call',
900
+ name: 'commandExecution',
901
+ arguments: {
902
+ command: item.command,
903
+ cwd: item.cwd,
904
+ status: item.status,
905
+ },
906
+ raw,
907
+ }];
908
+ }
909
+ if (type === 'fileChange') {
910
+ return [{
911
+ type: 'tool.call',
912
+ name: 'fileChange',
913
+ arguments: {
914
+ status: item.status,
915
+ },
916
+ raw,
917
+ }];
918
+ }
919
+ if (type === 'mcpToolCall' || type === 'dynamicToolCall') {
920
+ return [{
921
+ type: 'tool.call',
922
+ name: typeof item.tool === 'string' ? item.tool : type,
923
+ arguments: (typeof item.arguments === 'object' && item.arguments !== null ? item.arguments : undefined),
924
+ raw,
925
+ }];
926
+ }
927
+ return [];
928
+ }
929
+ export function normalizeCodexAppServerNotification(message) {
930
+ const method = typeof message.method === 'string' ? message.method : '';
931
+ const params = (typeof message.params === 'object' && message.params !== null
932
+ ? message.params
933
+ : {});
934
+ if (method === 'thread/started') {
935
+ return [{
936
+ type: 'session.note',
937
+ message: 'Thread started',
938
+ raw: message,
939
+ }];
940
+ }
941
+ if (method === 'thread/status/changed') {
942
+ const status = (typeof params.status === 'object' && params.status !== null ? params.status : {});
943
+ const statusType = typeof status.type === 'string' ? status.type : 'unknown';
944
+ return [{
945
+ type: 'session.note',
946
+ message: `Thread status · ${statusType}`,
947
+ raw: message,
948
+ }];
949
+ }
950
+ if (method === 'turn/started') {
951
+ return [{
952
+ type: 'session.note',
953
+ message: 'Turn started',
954
+ raw: message,
955
+ }];
956
+ }
957
+ if (method === 'item/agentMessage/delta' && typeof params.delta === 'string') {
958
+ return [{ type: 'assistant.delta', text: params.delta, raw: message }];
959
+ }
960
+ if (method === 'turn/completed') {
961
+ return [{ type: 'assistant.done', raw: message }];
962
+ }
963
+ if ((method === 'item/commandExecution/outputDelta' || method === 'item/reasoning/textDelta' || method === 'item/reasoning/summaryTextDelta')
964
+ && typeof params.delta === 'string') {
965
+ return [{ type: 'session.note', message: params.delta, raw: message }];
966
+ }
967
+ if (method === 'item/started' || method === 'item/completed') {
968
+ const item = (typeof params.item === 'object' && params.item !== null ? params.item : {});
969
+ return normalizeCodexThreadItem(item, message);
970
+ }
971
+ if (method === 'error') {
972
+ return [{
973
+ type: 'session.note',
974
+ message: extractWatchJsonText(params.error) || 'Codex error',
975
+ raw: message,
976
+ }];
977
+ }
978
+ return [];
979
+ }
980
+ export function normalizeCodexAppServerRequest(message) {
981
+ const method = typeof message.method === 'string' ? message.method : '';
982
+ const params = (typeof message.params === 'object' && message.params !== null
983
+ ? message.params
984
+ : {});
985
+ const interaction = normalizeCodexAppServerInteractionRequest(message);
986
+ if (interaction?.kind === 'user-input') {
987
+ return [{
988
+ type: 'input.required',
989
+ message: interaction.message,
990
+ request: interaction,
991
+ raw: message,
992
+ }];
993
+ }
994
+ if (interaction) {
995
+ const events = [{
996
+ type: 'approval.required',
997
+ message: interaction.message,
998
+ request: interaction,
999
+ raw: message,
1000
+ }];
1001
+ if (interaction.kind === 'command-approval' && interaction.command) {
1002
+ events.push({
1003
+ type: 'tool.call',
1004
+ name: 'commandExecution',
1005
+ arguments: {
1006
+ command: interaction.command,
1007
+ cwd: interaction.cwd,
1008
+ },
1009
+ raw: message,
1010
+ });
1011
+ }
1012
+ return events;
1013
+ }
1014
+ if (method === 'item/tool/call') {
1015
+ return [{
1016
+ type: 'tool.call',
1017
+ name: typeof params.tool === 'string' ? params.tool : 'dynamicToolCall',
1018
+ arguments: (typeof params.arguments === 'object' && params.arguments !== null ? params.arguments : undefined),
1019
+ raw: message,
1020
+ }];
1021
+ }
1022
+ return [];
1023
+ }
1024
+ export function getWatchArchiveRelativePaths(sessionId) {
1025
+ const normalizedId = sessionId.trim();
1026
+ const sessionDir = `${WATCH_SESSIONS_DIRNAME}/${normalizedId}`;
1027
+ return {
1028
+ sessionDir,
1029
+ sessionFile: `${sessionDir}/${WATCH_SESSION_FILE_NAME}`,
1030
+ eventsFile: `${sessionDir}/${WATCH_EVENTS_FILE_NAME}`,
1031
+ };
1032
+ }
1033
+ export function buildWatchThreadMetadata(record) {
1034
+ return {
1035
+ kind: 'watch',
1036
+ delegatedTo: 'secretary',
1037
+ sessionId: record.id,
1038
+ backend: record.backend,
1039
+ runtime: record.runtime,
1040
+ transport: record.transport,
1041
+ mode: record.mode,
1042
+ cwd: record.cwd,
1043
+ model: record.model,
1044
+ credentialSource: record.credentialSource,
1045
+ resolvedCredentialSource: record.resolvedCredentialSource,
1046
+ approvalSource: record.approvalSource,
1047
+ status: record.status,
1048
+ backendSessionId: record.backendSessionId,
1049
+ };
1050
+ }
1051
+ function pushWatchTranscriptMessage(messages, role, content, createdAt) {
1052
+ const normalized = content?.replace(/\r/g, '').trimEnd();
1053
+ if (!normalized) {
1054
+ return;
1055
+ }
1056
+ messages.push({
1057
+ role,
1058
+ content: normalized,
1059
+ createdAt,
1060
+ });
1061
+ }
1062
+ function flushWatchAssistantMessage(messages, state, fallbackTimestamp) {
1063
+ if (!state.assistantText.trim()) {
1064
+ state.assistantText = '';
1065
+ state.assistantTimestamp = undefined;
1066
+ return;
1067
+ }
1068
+ pushWatchTranscriptMessage(messages, 'assistant', state.assistantText, state.assistantTimestamp ?? fallbackTimestamp);
1069
+ state.assistantText = '';
1070
+ state.assistantTimestamp = undefined;
1071
+ }
1072
+ function appendWatchTranscriptEvent(messages, state, entry, event) {
1073
+ if (event.type === 'assistant.delta') {
1074
+ if (!state.assistantTimestamp) {
1075
+ state.assistantTimestamp = entry.timestamp;
1076
+ }
1077
+ state.assistantText += event.text;
1078
+ return;
1079
+ }
1080
+ if (event.type === 'assistant.done') {
1081
+ if (event.text && !state.assistantText) {
1082
+ pushWatchTranscriptMessage(messages, 'assistant', event.text, entry.timestamp);
1083
+ return;
1084
+ }
1085
+ flushWatchAssistantMessage(messages, state, entry.timestamp);
1086
+ return;
1087
+ }
1088
+ flushWatchAssistantMessage(messages, state, entry.timestamp);
1089
+ if (event.type === 'tool.call') {
1090
+ pushWatchTranscriptMessage(messages, 'system', `[tool] ${event.name}${event.arguments ? ` ${JSON.stringify(event.arguments)}` : ''}`, entry.timestamp);
1091
+ return;
1092
+ }
1093
+ if (event.type === 'approval.required') {
1094
+ pushWatchTranscriptMessage(messages, 'system', `[approval] ${event.message}`, entry.timestamp);
1095
+ return;
1096
+ }
1097
+ if (event.type === 'input.required') {
1098
+ pushWatchTranscriptMessage(messages, 'system', `[input] ${event.message}`, entry.timestamp);
1099
+ return;
1100
+ }
1101
+ pushWatchTranscriptMessage(messages, 'system', `[note] ${event.message}`, entry.timestamp);
1102
+ }
1103
+ function appendWatchTranscriptRawEntry(messages, entry) {
1104
+ const trimmed = entry.line.trim();
1105
+ if (!trimmed) {
1106
+ return;
1107
+ }
1108
+ try {
1109
+ const parsed = JSON.parse(trimmed);
1110
+ const type = typeof parsed.type === 'string' ? parsed.type : '';
1111
+ if (type === 'user.turn' && typeof parsed.text === 'string') {
1112
+ pushWatchTranscriptMessage(messages, 'user', parsed.text, entry.timestamp);
1113
+ return;
1114
+ }
1115
+ if (type === 'turn.start') {
1116
+ const command = typeof parsed.command === 'string' ? parsed.command : 'unknown';
1117
+ const args = Array.isArray(parsed.args)
1118
+ ? parsed.args.filter((value) => typeof value === 'string')
1119
+ : [];
1120
+ pushWatchTranscriptMessage(messages, 'system', `[turn] ${[command, ...args].join(' ').trim()}`, entry.timestamp);
1121
+ return;
1122
+ }
1123
+ if (type === 'credentials.resolve') {
1124
+ const requested = typeof parsed.requestedCredentialSource === 'string' ? parsed.requestedCredentialSource : 'auto';
1125
+ const resolved = typeof parsed.resolvedCredentialSource === 'string' ? parsed.resolvedCredentialSource : requested;
1126
+ pushWatchTranscriptMessage(messages, 'system', `[credentials] ${requested} -> ${resolved}`, entry.timestamp);
1127
+ return;
1128
+ }
1129
+ if (type === 'process.error' && typeof parsed.message === 'string') {
1130
+ pushWatchTranscriptMessage(messages, 'system', `[error] ${parsed.message}`, entry.timestamp);
1131
+ return;
1132
+ }
1133
+ }
1134
+ catch {
1135
+ // Keep original line when it is not structured JSON.
1136
+ }
1137
+ pushWatchTranscriptMessage(messages, 'system', entry.stream === 'stderr' ? `stderr> ${trimmed}` : trimmed, entry.timestamp);
1138
+ }
1139
+ export function buildWatchTranscriptMessages(entries) {
1140
+ const messages = [];
1141
+ const state = {
1142
+ assistantText: '',
1143
+ };
1144
+ for (const entry of entries) {
1145
+ if (entry.events.length > 0) {
1146
+ for (const event of entry.events) {
1147
+ appendWatchTranscriptEvent(messages, state, entry, event);
1148
+ }
1149
+ continue;
1150
+ }
1151
+ flushWatchAssistantMessage(messages, state, entry.timestamp);
1152
+ appendWatchTranscriptRawEntry(messages, entry);
1153
+ }
1154
+ flushWatchAssistantMessage(messages, state, new Date().toISOString());
1155
+ return messages;
1156
+ }