@agentsquared/cli 1.0.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.
@@ -0,0 +1,344 @@
1
+ import fs from 'node:fs'
2
+ import path from 'node:path'
3
+
4
+ function clean(value) {
5
+ return `${value ?? ''}`.trim()
6
+ }
7
+
8
+ function ensureDir(dir) {
9
+ fs.mkdirSync(dir, { recursive: true })
10
+ }
11
+
12
+ function safeSegment(value, fallback = 'unknown') {
13
+ const cleaned = clean(value).replace(/[^a-zA-Z0-9_.-]+/g, '_')
14
+ return cleaned || fallback
15
+ }
16
+
17
+ function nowISO() {
18
+ return new Date().toISOString()
19
+ }
20
+
21
+ function clone(value) {
22
+ return JSON.parse(JSON.stringify(value))
23
+ }
24
+
25
+ function jsonRead(filePath, fallback) {
26
+ try {
27
+ if (!fs.existsSync(filePath)) {
28
+ return clone(fallback)
29
+ }
30
+ return JSON.parse(fs.readFileSync(filePath, 'utf8'))
31
+ } catch {
32
+ return clone(fallback)
33
+ }
34
+ }
35
+
36
+ function jsonWrite(filePath, payload) {
37
+ ensureDir(path.dirname(filePath))
38
+ fs.writeFileSync(filePath, `${JSON.stringify(payload, null, 2)}\n`)
39
+ }
40
+
41
+ function extractInboundText(item = null) {
42
+ const parts = item?.request?.params?.message?.parts ?? []
43
+ return parts
44
+ .filter((part) => clean(part?.kind) === 'text')
45
+ .map((part) => clean(part?.text))
46
+ .filter(Boolean)
47
+ .join('\n')
48
+ .trim()
49
+ }
50
+
51
+ function extractReplyText(peerResponse = null) {
52
+ const parts = peerResponse?.message?.parts ?? []
53
+ return parts
54
+ .filter((part) => clean(part?.kind) === 'text')
55
+ .map((part) => clean(part?.text))
56
+ .filter(Boolean)
57
+ .join('\n')
58
+ .trim()
59
+ }
60
+
61
+ function extractConversationKey(item = null, peerResponse = null, ownerReport = null) {
62
+ return clean(
63
+ peerResponse?.metadata?.conversationKey
64
+ ?? item?.request?.params?.metadata?.conversationKey
65
+ ?? ownerReport?.conversationKey
66
+ )
67
+ }
68
+
69
+ function excerpt(text, maxLength = 180) {
70
+ const compact = clean(text).replace(/\s+/g, ' ').trim()
71
+ if (!compact) {
72
+ return ''
73
+ }
74
+ return compact.length > maxLength ? `${compact.slice(0, maxLength - 3)}...` : compact
75
+ }
76
+
77
+ function requestIdFromEntry(entry = null) {
78
+ return clean(entry?.request?.id)
79
+ }
80
+
81
+ function isFinalOwnerReport(entry = null) {
82
+ return Boolean(entry?.ownerReport?.finalize)
83
+ }
84
+
85
+ export function createInboxStore({
86
+ inboxDir
87
+ } = {}) {
88
+ const resolvedInboxDir = path.resolve(clean(inboxDir) || '.')
89
+ const entriesDir = path.join(resolvedInboxDir, 'entries')
90
+ const indexFile = path.join(resolvedInboxDir, 'index.json')
91
+ const inboxMarkdownFile = path.join(resolvedInboxDir, 'inbox.md')
92
+
93
+ function readIndex() {
94
+ return jsonRead(indexFile, {
95
+ updatedAt: '',
96
+ totalCount: 0,
97
+ lastEntryAt: '',
98
+ lastEntryId: '',
99
+ ownerPushAttemptedCount: 0,
100
+ ownerPushDeliveredCount: 0,
101
+ ownerPushFailedCount: 0,
102
+ recent: []
103
+ })
104
+ }
105
+
106
+ function summarizeEntry(entry) {
107
+ return {
108
+ id: entry.id,
109
+ createdAt: entry.createdAt,
110
+ updatedAt: entry.updatedAt,
111
+ remoteAgentId: entry.remoteAgentId,
112
+ conversationKey: entry.conversationKey,
113
+ peerSessionId: entry.peerSessionId,
114
+ selectedSkill: entry.selectedSkill,
115
+ summary: entry.summary,
116
+ messageExcerpt: entry.messageExcerpt,
117
+ replyExcerpt: entry.replyExcerpt,
118
+ ownerDelivery: entry.ownerDelivery ?? null,
119
+ file: entry.file
120
+ }
121
+ }
122
+
123
+ function renderMarkdown(index) {
124
+ const lines = [
125
+ '# Inbox',
126
+ '',
127
+ `Updated: ${clean(index.updatedAt) || 'unknown'}`,
128
+ `Total: ${index.totalCount ?? 0}`,
129
+ `Last Entry: ${clean(index.lastEntryAt) || 'none'}`,
130
+ `Owner Push Attempted: ${index.ownerPushAttemptedCount ?? 0}`,
131
+ `Owner Push Delivered: ${index.ownerPushDeliveredCount ?? 0}`,
132
+ `Owner Push Failed: ${index.ownerPushFailedCount ?? 0}`,
133
+ ''
134
+ ]
135
+
136
+ if (Array.isArray(index.recent) && index.recent.length > 0) {
137
+ lines.push('## Recent')
138
+ lines.push('')
139
+ for (const item of index.recent) {
140
+ const delivery = item.ownerDelivery?.attempted
141
+ ? item.ownerDelivery?.delivered
142
+ ? 'owner-delivered'
143
+ : 'owner-push-failed'
144
+ : 'audit-only'
145
+ lines.push(`- [${delivery}] ${item.remoteAgentId}: ${item.summary}`)
146
+ }
147
+ lines.push('')
148
+ } else {
149
+ lines.push('## Recent')
150
+ lines.push('')
151
+ lines.push('- none')
152
+ lines.push('')
153
+ }
154
+
155
+ fs.writeFileSync(inboxMarkdownFile, `${lines.join('\n')}\n`)
156
+ }
157
+
158
+ function rebuildIndex() {
159
+ ensureDir(entriesDir)
160
+ const files = fs.readdirSync(entriesDir)
161
+ .filter((name) => name.endsWith('.json'))
162
+ .sort()
163
+
164
+ const entries = files.map((name) => {
165
+ const entry = jsonRead(path.join(entriesDir, name), null)
166
+ return entry && typeof entry === 'object' ? entry : null
167
+ }).filter(Boolean)
168
+
169
+ entries.sort((left, right) => {
170
+ return Date.parse(right.createdAt || 0) - Date.parse(left.createdAt || 0)
171
+ })
172
+
173
+ const recent = entries.slice(0, 50).map(summarizeEntry)
174
+ const ownerPushAttemptedCount = entries.filter((entry) => Boolean(entry?.ownerDelivery?.attempted)).length
175
+ const ownerPushDeliveredCount = entries.filter((entry) => entry?.ownerDelivery?.attempted && entry?.ownerDelivery?.delivered).length
176
+ const ownerPushFailedCount = entries.filter((entry) => entry?.ownerDelivery?.attempted && !entry?.ownerDelivery?.delivered).length
177
+
178
+ const index = {
179
+ updatedAt: nowISO(),
180
+ totalCount: entries.length,
181
+ lastEntryAt: clean(entries[0]?.createdAt),
182
+ lastEntryId: clean(entries[0]?.id),
183
+ ownerPushAttemptedCount,
184
+ ownerPushDeliveredCount,
185
+ ownerPushFailedCount,
186
+ recent
187
+ }
188
+ jsonWrite(indexFile, index)
189
+ renderMarkdown(index)
190
+ return index
191
+ }
192
+
193
+ function appendEntry({
194
+ agentId,
195
+ selectedSkill,
196
+ mailboxKey,
197
+ item,
198
+ ownerReport,
199
+ peerResponse,
200
+ ownerDelivery = null
201
+ }) {
202
+ ensureDir(entriesDir)
203
+ const createdAt = nowISO()
204
+ const id = clean(item?.inboundId) || safeSegment(createdAt)
205
+ const remoteAgentId = clean(item?.remoteAgentId)
206
+ const normalizedRequestId = clean(item?.request?.id)
207
+ const normalizedConversationKey = extractConversationKey(item, peerResponse, ownerReport)
208
+ const isFinalReport = isFinalOwnerReport({ ownerReport })
209
+ let existingEntry = null
210
+ let file = ''
211
+ const files = fs.readdirSync(entriesDir)
212
+ .filter((name) => name.endsWith('.json'))
213
+ .sort()
214
+ .reverse()
215
+ if (normalizedRequestId) {
216
+ for (const name of files) {
217
+ const candidatePath = path.join(entriesDir, name)
218
+ const candidate = jsonRead(candidatePath, null)
219
+ if (!candidate || typeof candidate !== 'object') {
220
+ continue
221
+ }
222
+ if (requestIdFromEntry(candidate) !== normalizedRequestId) {
223
+ continue
224
+ }
225
+ if (clean(candidate.peerSessionId) && clean(item?.peerSessionId) && clean(candidate.peerSessionId) !== clean(item?.peerSessionId)) {
226
+ continue
227
+ }
228
+ existingEntry = candidate
229
+ file = candidatePath
230
+ break
231
+ }
232
+ }
233
+ if (!file && isFinalReport && normalizedConversationKey) {
234
+ for (const name of files) {
235
+ const candidatePath = path.join(entriesDir, name)
236
+ const candidate = jsonRead(candidatePath, null)
237
+ if (!candidate || typeof candidate !== 'object') {
238
+ continue
239
+ }
240
+ if (clean(candidate.conversationKey) !== normalizedConversationKey) {
241
+ continue
242
+ }
243
+ if (!isFinalOwnerReport(candidate)) {
244
+ continue
245
+ }
246
+ existingEntry = candidate
247
+ file = candidatePath
248
+ break
249
+ }
250
+ }
251
+ if (!file) {
252
+ const filename = `${createdAt.replace(/[:.]/g, '-')}_${safeSegment(remoteAgentId)}_${safeSegment(id)}.json`
253
+ file = path.join(entriesDir, filename)
254
+ }
255
+ const inboundText = extractInboundText(item)
256
+ const replyText = extractReplyText(peerResponse)
257
+ const summary = clean(ownerReport?.summary) || `${remoteAgentId || 'unknown'} opened an inbound ${selectedSkill} session.`
258
+
259
+ const entry = {
260
+ ...(existingEntry ?? {}),
261
+ id: clean(existingEntry?.id) || id,
262
+ createdAt: clean(existingEntry?.createdAt) || createdAt,
263
+ updatedAt: createdAt,
264
+ agentId: clean(agentId),
265
+ mailboxKey: clean(mailboxKey),
266
+ remoteAgentId,
267
+ conversationKey: normalizedConversationKey,
268
+ peerSessionId: clean(item?.peerSessionId),
269
+ selectedSkill: clean(selectedSkill),
270
+ summary,
271
+ messageExcerpt: excerpt(inboundText),
272
+ replyExcerpt: excerpt(replyText),
273
+ ownerReport: ownerReport ?? null,
274
+ ownerDelivery: ownerDelivery ?? null,
275
+ request: item?.request ?? null,
276
+ peerResponse: peerResponse ?? null,
277
+ file: path.relative(resolvedInboxDir, file)
278
+ }
279
+
280
+ jsonWrite(file, entry)
281
+ const index = rebuildIndex()
282
+ return {
283
+ entry,
284
+ index
285
+ }
286
+ }
287
+
288
+ function findDeliveredFinalConversationReport(conversationKey = '') {
289
+ const targetConversationKey = clean(conversationKey)
290
+ if (!targetConversationKey) {
291
+ return null
292
+ }
293
+ ensureDir(entriesDir)
294
+ const files = fs.readdirSync(entriesDir)
295
+ .filter((name) => name.endsWith('.json'))
296
+ .sort()
297
+ .reverse()
298
+
299
+ for (const name of files) {
300
+ const entry = jsonRead(path.join(entriesDir, name), null)
301
+ if (!entry || typeof entry !== 'object') {
302
+ continue
303
+ }
304
+ if (clean(entry.conversationKey) !== targetConversationKey) {
305
+ continue
306
+ }
307
+ if (!entry.ownerDelivery?.delivered) {
308
+ continue
309
+ }
310
+ if (!entry.ownerReport?.finalize) {
311
+ continue
312
+ }
313
+ return entry
314
+ }
315
+ return null
316
+ }
317
+
318
+ function snapshot() {
319
+ const index = readIndex()
320
+ return {
321
+ inboxDir: resolvedInboxDir,
322
+ entriesDir,
323
+ inboxMarkdownFile,
324
+ indexFile,
325
+ totalCount: index.totalCount ?? 0,
326
+ lastEntryAt: index.lastEntryAt ?? '',
327
+ ownerPushAttemptedCount: index.ownerPushAttemptedCount ?? 0,
328
+ ownerPushDeliveredCount: index.ownerPushDeliveredCount ?? 0,
329
+ ownerPushFailedCount: index.ownerPushFailedCount ?? 0
330
+ }
331
+ }
332
+
333
+ return {
334
+ inboxDir: resolvedInboxDir,
335
+ entriesDir,
336
+ indexFile,
337
+ inboxMarkdownFile,
338
+ readIndex,
339
+ rebuildIndex,
340
+ appendEntry,
341
+ findDeliveredFinalConversationReport,
342
+ snapshot
343
+ }
344
+ }