@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.
- package/LICENSE +21 -0
- package/README.md +420 -0
- package/a2_cli.mjs +1576 -0
- package/adapters/index.mjs +79 -0
- package/adapters/openclaw/adapter.mjs +1020 -0
- package/adapters/openclaw/cli.mjs +89 -0
- package/adapters/openclaw/detect.mjs +259 -0
- package/adapters/openclaw/helpers.mjs +827 -0
- package/adapters/openclaw/ws_client.mjs +740 -0
- package/bin/a2-cli.js +8 -0
- package/lib/conversation/policy.mjs +122 -0
- package/lib/conversation/store.mjs +223 -0
- package/lib/conversation/templates.mjs +419 -0
- package/lib/gateway/api.mjs +28 -0
- package/lib/gateway/inbox.mjs +344 -0
- package/lib/gateway/lifecycle.mjs +602 -0
- package/lib/gateway/runtime_state.mjs +388 -0
- package/lib/gateway/server.mjs +883 -0
- package/lib/gateway/state.mjs +175 -0
- package/lib/routing/agent_router.mjs +511 -0
- package/lib/runtime/executor.mjs +380 -0
- package/lib/runtime/keys.mjs +85 -0
- package/lib/runtime/report.mjs +302 -0
- package/lib/runtime/safety.mjs +72 -0
- package/lib/shared/paths.mjs +155 -0
- package/lib/shared/primitives.mjs +43 -0
- package/lib/transport/http_json.mjs +96 -0
- package/lib/transport/libp2p.mjs +397 -0
- package/lib/transport/peer_session.mjs +857 -0
- package/lib/transport/relay_http.mjs +110 -0
- package/package.json +53 -0
|
@@ -0,0 +1,419 @@
|
|
|
1
|
+
function clean(value) {
|
|
2
|
+
return `${value ?? ''}`.trim()
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
function block(label, value) {
|
|
6
|
+
return [`${label}:`, clean(value) || '(empty)'].join('\n')
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
function quote(text) {
|
|
10
|
+
const lines = clean(text).split('\n').filter(Boolean)
|
|
11
|
+
if (lines.length === 0) {
|
|
12
|
+
return '> (empty)'
|
|
13
|
+
}
|
|
14
|
+
return lines.map((line) => `> ${line}`).join('\n')
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function excerpt(text, maxLength = 280) {
|
|
18
|
+
const compact = clean(text).replace(/\s+/g, ' ').trim()
|
|
19
|
+
if (!compact) {
|
|
20
|
+
return ''
|
|
21
|
+
}
|
|
22
|
+
return compact.length > maxLength ? `${compact.slice(0, maxLength - 3)}...` : compact
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function normalizeTurnOutline(turnOutline = []) {
|
|
26
|
+
if (!Array.isArray(turnOutline)) {
|
|
27
|
+
return []
|
|
28
|
+
}
|
|
29
|
+
return turnOutline.map((item, index) => {
|
|
30
|
+
if (item && typeof item === 'object') {
|
|
31
|
+
return {
|
|
32
|
+
turnIndex: Number.parseInt(`${item.turnIndex ?? index + 1}`, 10) || index + 1,
|
|
33
|
+
summary: clean(item.summary || item.text || item.value)
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
return {
|
|
37
|
+
turnIndex: index + 1,
|
|
38
|
+
summary: clean(item)
|
|
39
|
+
}
|
|
40
|
+
}).filter((item) => item.summary)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function ensureTurnOutline(turnOutline = [], turnCount = 1, {
|
|
44
|
+
language = 'en',
|
|
45
|
+
fallbackSummary = ''
|
|
46
|
+
} = {}) {
|
|
47
|
+
const normalized = normalizeTurnOutline(turnOutline)
|
|
48
|
+
if (normalized.length > 0) {
|
|
49
|
+
return normalized
|
|
50
|
+
}
|
|
51
|
+
const count = Math.max(1, Number.parseInt(`${turnCount ?? 1}`, 10) || 1)
|
|
52
|
+
const summary = clean(fallbackSummary) || 'Received the message and completed this turn.'
|
|
53
|
+
return Array.from({ length: count }, (_, index) => ({
|
|
54
|
+
turnIndex: index + 1,
|
|
55
|
+
summary
|
|
56
|
+
}))
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function section(label) {
|
|
60
|
+
return `**${clean(label)}**`
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function logo(language = 'en') {
|
|
64
|
+
return '🅰️✌️'
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function isChineseLanguage(language = '') {
|
|
68
|
+
return clean(language).toLowerCase().startsWith('zh')
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function containsHanText(text = '') {
|
|
72
|
+
return /[\p{Script=Han}]/u.test(clean(text))
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export function inferOwnerFacingLanguage(...values) {
|
|
76
|
+
return values.some((value) => containsHanText(value)) ? 'zh-CN' : 'en'
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function resolveTimeZone(timeZone = '') {
|
|
80
|
+
return clean(timeZone) || Intl.DateTimeFormat().resolvedOptions().timeZone || 'UTC'
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function formatDisplayTime(value, {
|
|
84
|
+
language = 'en',
|
|
85
|
+
timeZone = '',
|
|
86
|
+
localTime = false
|
|
87
|
+
} = {}) {
|
|
88
|
+
const raw = clean(value)
|
|
89
|
+
if (!raw) {
|
|
90
|
+
return 'unknown'
|
|
91
|
+
}
|
|
92
|
+
if (!localTime) {
|
|
93
|
+
return raw
|
|
94
|
+
}
|
|
95
|
+
const parsed = new Date(raw)
|
|
96
|
+
if (Number.isNaN(parsed.getTime())) {
|
|
97
|
+
return raw
|
|
98
|
+
}
|
|
99
|
+
const resolvedTimeZone = resolveTimeZone(timeZone)
|
|
100
|
+
const locale = 'en-CA'
|
|
101
|
+
const formatter = new Intl.DateTimeFormat(locale, {
|
|
102
|
+
timeZone: resolvedTimeZone,
|
|
103
|
+
hour12: false,
|
|
104
|
+
year: 'numeric',
|
|
105
|
+
month: '2-digit',
|
|
106
|
+
day: '2-digit',
|
|
107
|
+
hour: '2-digit',
|
|
108
|
+
minute: '2-digit',
|
|
109
|
+
second: '2-digit'
|
|
110
|
+
})
|
|
111
|
+
const parts = Object.fromEntries(formatter.formatToParts(parsed).map((part) => [part.type, part.value]))
|
|
112
|
+
const normalized = `${parts.year ?? '0000'}-${parts.month ?? '00'}-${parts.day ?? '00'} ${parts.hour ?? '00'}:${parts.minute ?? '00'}:${parts.second ?? '00'}`
|
|
113
|
+
return `${normalized} (${resolvedTimeZone})`
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export function renderOwnerFacingReport(report = null) {
|
|
117
|
+
if (!report || typeof report !== 'object') {
|
|
118
|
+
return clean(report)
|
|
119
|
+
}
|
|
120
|
+
const title = clean(report.title)
|
|
121
|
+
const summary = clean(report.summary)
|
|
122
|
+
const message = clean(report.message)
|
|
123
|
+
return [title, message || summary].filter(Boolean).join('\n\n').trim()
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
export function peerResponseText(peerResponse = null) {
|
|
127
|
+
const target = unwrapJsonRpcResult(peerResponse)
|
|
128
|
+
const parts = target?.message?.parts ?? target?.parts ?? []
|
|
129
|
+
return parts
|
|
130
|
+
.filter((part) => clean(part?.kind) === 'text')
|
|
131
|
+
.map((part) => clean(part?.text))
|
|
132
|
+
.filter(Boolean)
|
|
133
|
+
.join('\n')
|
|
134
|
+
.trim()
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function unwrapJsonRpcResult(value = null) {
|
|
138
|
+
if (!value || typeof value !== 'object') {
|
|
139
|
+
return value
|
|
140
|
+
}
|
|
141
|
+
if (value.result && typeof value.result === 'object') {
|
|
142
|
+
return value.result
|
|
143
|
+
}
|
|
144
|
+
return value
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
export function parseAgentSquaredOutboundEnvelope(text = '') {
|
|
148
|
+
const raw = clean(text)
|
|
149
|
+
if (!raw.includes('[AgentSquared]')) {
|
|
150
|
+
return null
|
|
151
|
+
}
|
|
152
|
+
const from = clean(raw.match(/^From:\s*(.+)$/m)?.[1])
|
|
153
|
+
const to = clean(raw.match(/^To:\s*(.+)$/m)?.[1])
|
|
154
|
+
const sentAt = clean(raw.match(/^Sent At \(UTC\):\s*(.+)$/m)?.[1])
|
|
155
|
+
const ownerRequest = extractDelimitedBlock(raw, 'A2_OWNER_REQUEST') || clean(raw.match(/Owner Request:\n([\s\S]*?)\n\nPlease reply to me for my owner\./)?.[1])
|
|
156
|
+
return {
|
|
157
|
+
from,
|
|
158
|
+
to,
|
|
159
|
+
sentAt,
|
|
160
|
+
ownerRequest
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
function extractDelimitedBlock(raw = '', key = '') {
|
|
165
|
+
const begin = `BEGIN_${clean(key)}`
|
|
166
|
+
const end = `END_${clean(key)}`
|
|
167
|
+
const startIndex = raw.indexOf(begin)
|
|
168
|
+
if (startIndex < 0) {
|
|
169
|
+
return ''
|
|
170
|
+
}
|
|
171
|
+
const contentStart = startIndex + begin.length
|
|
172
|
+
const endIndex = raw.indexOf(end, contentStart)
|
|
173
|
+
if (endIndex < 0) {
|
|
174
|
+
return ''
|
|
175
|
+
}
|
|
176
|
+
return clean(raw.slice(contentStart, endIndex))
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
export function buildSkillOutboundText({
|
|
180
|
+
localAgentId,
|
|
181
|
+
targetAgentId,
|
|
182
|
+
skillName,
|
|
183
|
+
originalText,
|
|
184
|
+
sentAt = new Date().toISOString(),
|
|
185
|
+
localSkillInventory = ''
|
|
186
|
+
} = {}) {
|
|
187
|
+
const selectedSkill = clean(skillName) || 'friend-im'
|
|
188
|
+
const skillLayer = selectedSkill === 'agent-mutual-learning'
|
|
189
|
+
? [
|
|
190
|
+
`I am ${clean(localAgentId) || 'an AgentSquared agent'} from AgentSquared. My owner asked me to start a mutual-learning exchange with you.`,
|
|
191
|
+
'Please answer in this exact order and keep it concrete.',
|
|
192
|
+
'1. ALL SKILLS: list your current actual skill names or workflow names as completely as you can verify.',
|
|
193
|
+
'2. MOST USED: list the ones you use most often.',
|
|
194
|
+
'3. RECENT: list any skills or workflows you installed or added recently.',
|
|
195
|
+
'4. DIFFERENT VS MY SNAPSHOT: compare your ALL SKILLS list against my local snapshot below and name the 1-3 concrete skills or workflows that are really different on your side.',
|
|
196
|
+
'5. FOR EACH DIFFERENT ITEM: explain what it is for, what problem it solves, and why it matters in practice.',
|
|
197
|
+
'My goal is to compare your full skill list against my full local skill list, find the concrete differences, and then ask follow-up questions only about the remote-only or meaningfully different items.',
|
|
198
|
+
'Please do not stay generic at the capability level if you can name the concrete skill or workflow directly.'
|
|
199
|
+
].join(' ')
|
|
200
|
+
: `I am ${clean(localAgentId) || 'an AgentSquared agent'} from AgentSquared. My owner asked me to send you a short private message.`
|
|
201
|
+
|
|
202
|
+
return [
|
|
203
|
+
'🅰️✌️ [AgentSquared]',
|
|
204
|
+
'This is an AgentSquared private agent message.',
|
|
205
|
+
'Please read the AgentSquared official skill before sending or replying through AgentSquared.',
|
|
206
|
+
`From: ${clean(localAgentId) || 'unknown'}`,
|
|
207
|
+
`To: ${clean(targetAgentId) || 'unknown'}`,
|
|
208
|
+
`Sent At (UTC): ${clean(sentAt) || 'unknown'}`,
|
|
209
|
+
'',
|
|
210
|
+
skillLayer,
|
|
211
|
+
'',
|
|
212
|
+
block('Owner Request', originalText),
|
|
213
|
+
...(selectedSkill === 'agent-mutual-learning' && clean(localSkillInventory)
|
|
214
|
+
? [
|
|
215
|
+
'',
|
|
216
|
+
block('Local Skill Snapshot', localSkillInventory)
|
|
217
|
+
]
|
|
218
|
+
: []),
|
|
219
|
+
'',
|
|
220
|
+
'BEGIN_A2_OWNER_REQUEST',
|
|
221
|
+
clean(originalText) || '(empty)',
|
|
222
|
+
'END_A2_OWNER_REQUEST',
|
|
223
|
+
'',
|
|
224
|
+
'Please reply to me for my owner.'
|
|
225
|
+
].join('\n')
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
export function buildSenderBaseReport({
|
|
229
|
+
localAgentId,
|
|
230
|
+
targetAgentId,
|
|
231
|
+
selectedSkill,
|
|
232
|
+
sentAt,
|
|
233
|
+
originalText,
|
|
234
|
+
sentText = '',
|
|
235
|
+
replyText,
|
|
236
|
+
replyAt,
|
|
237
|
+
conversationKey,
|
|
238
|
+
peerSessionId,
|
|
239
|
+
turnCount = 1,
|
|
240
|
+
stopReason = '',
|
|
241
|
+
detailsHint = '',
|
|
242
|
+
overallSummary = '',
|
|
243
|
+
actionItems = [],
|
|
244
|
+
turnOutline = [],
|
|
245
|
+
language = 'en',
|
|
246
|
+
timeZone = '',
|
|
247
|
+
localTime = false
|
|
248
|
+
} = {}) {
|
|
249
|
+
const safeReplyText = clean(replyText)
|
|
250
|
+
const title = `**${logo(language)} AgentSquared message delivered**`
|
|
251
|
+
const normalizedTurnOutline = ensureTurnOutline(turnOutline, turnCount, {
|
|
252
|
+
language,
|
|
253
|
+
fallbackSummary: 'Completed this turn of the conversation.'
|
|
254
|
+
})
|
|
255
|
+
const normalizedOverallSummary = clean(overallSummary) || (safeReplyText ? excerpt(safeReplyText) : 'This conversation completed.')
|
|
256
|
+
const displayedSentText = clean(sentText) || clean(originalText)
|
|
257
|
+
const normalizedActionItems = Array.isArray(actionItems)
|
|
258
|
+
? actionItems.map((item) => clean(item)).filter(Boolean)
|
|
259
|
+
: []
|
|
260
|
+
const message = [
|
|
261
|
+
section('Outbound message'),
|
|
262
|
+
`- Sender: ${clean(localAgentId) || 'unknown'}`,
|
|
263
|
+
`- Recipient: ${clean(targetAgentId) || 'unknown'}`,
|
|
264
|
+
`- Sent At${localTime ? ' (Local Time)' : ' (UTC)'}: ${formatDisplayTime(sentAt, { language, timeZone, localTime })}`,
|
|
265
|
+
...(clean(conversationKey) ? [`- Conversation Key: ${clean(conversationKey)}`] : []),
|
|
266
|
+
...(clean(peerSessionId) ? [`- Transport Session: ${clean(peerSessionId)}`] : []),
|
|
267
|
+
`- Skill Hint: ${clean(selectedSkill) || 'friend-im'}`,
|
|
268
|
+
'',
|
|
269
|
+
section('Content sent'),
|
|
270
|
+
quote(displayedSentText),
|
|
271
|
+
'',
|
|
272
|
+
section('Overall summary'),
|
|
273
|
+
`- ${normalizedOverallSummary}`,
|
|
274
|
+
'',
|
|
275
|
+
section('Detailed conversation'),
|
|
276
|
+
...normalizedTurnOutline.map((item) => `- Turn ${item.turnIndex}: ${item.summary}`),
|
|
277
|
+
'',
|
|
278
|
+
section('Actions taken'),
|
|
279
|
+
`- Sent the requested AgentSquared message to ${clean(targetAgentId) || 'the remote agent'}.`,
|
|
280
|
+
`- Total turns: ${turnCount}.`,
|
|
281
|
+
`- Received the final peer reply at ${formatDisplayTime(replyAt, { language, timeZone, localTime })}.`,
|
|
282
|
+
...(clean(stopReason) ? [`- Stopped with reason: ${clean(stopReason)}.`] : []),
|
|
283
|
+
...normalizedActionItems.map((item) => `- ${item}`),
|
|
284
|
+
'',
|
|
285
|
+
'---',
|
|
286
|
+
'',
|
|
287
|
+
'---',
|
|
288
|
+
'',
|
|
289
|
+
...(clean(detailsHint) ? [detailsHint, ''] : []),
|
|
290
|
+
'For raw turn-by-turn details, check the conversation output or the local inbox.'
|
|
291
|
+
].join('\n')
|
|
292
|
+
return {
|
|
293
|
+
title,
|
|
294
|
+
summary: `${clean(targetAgentId) || 'The remote agent'} replied through AgentSquared.`,
|
|
295
|
+
message
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
export function buildSenderFailureReport({
|
|
300
|
+
localAgentId,
|
|
301
|
+
targetAgentId,
|
|
302
|
+
selectedSkill,
|
|
303
|
+
sentAt,
|
|
304
|
+
originalText,
|
|
305
|
+
conversationKey,
|
|
306
|
+
deliveryStatus = '',
|
|
307
|
+
failureStage = '',
|
|
308
|
+
confirmationLevel = '',
|
|
309
|
+
failureCode = '',
|
|
310
|
+
failureReason = '',
|
|
311
|
+
failureDetail = '',
|
|
312
|
+
nextStep = '',
|
|
313
|
+
language = 'en',
|
|
314
|
+
timeZone = '',
|
|
315
|
+
localTime = false
|
|
316
|
+
} = {}) {
|
|
317
|
+
const title = `**${logo(language)} AgentSquared message failed**`
|
|
318
|
+
const message = [
|
|
319
|
+
section('Outbound message'),
|
|
320
|
+
`- Sender: ${clean(localAgentId) || 'unknown'}`,
|
|
321
|
+
`- Intended Recipient: ${clean(targetAgentId) || 'unknown'}`,
|
|
322
|
+
`- Sent At${localTime ? ' (Local Time)' : ' (UTC)'}: ${formatDisplayTime(sentAt, { language, timeZone, localTime })}`,
|
|
323
|
+
...(clean(conversationKey) ? [`- Conversation Key: ${clean(conversationKey)}`] : []),
|
|
324
|
+
`- Skill Hint: ${clean(selectedSkill) || 'friend-im'}`,
|
|
325
|
+
'',
|
|
326
|
+
section('Content'),
|
|
327
|
+
quote(originalText),
|
|
328
|
+
'',
|
|
329
|
+
'---',
|
|
330
|
+
'',
|
|
331
|
+
section('Delivery result'),
|
|
332
|
+
`- Status: ${clean(deliveryStatus) || 'failed'}`,
|
|
333
|
+
`- Failure Code: ${clean(failureCode) || 'delivery-failed'}`,
|
|
334
|
+
...(clean(failureStage) ? [`- Failure Stage: ${clean(failureStage)}`] : []),
|
|
335
|
+
...(clean(confirmationLevel) ? [`- Confirmation Level: ${clean(confirmationLevel)}`] : []),
|
|
336
|
+
'',
|
|
337
|
+
section('Failure reason'),
|
|
338
|
+
quote(failureReason),
|
|
339
|
+
...(clean(failureDetail)
|
|
340
|
+
? [
|
|
341
|
+
'',
|
|
342
|
+
section('Failure detail'),
|
|
343
|
+
quote(failureDetail)
|
|
344
|
+
]
|
|
345
|
+
: []),
|
|
346
|
+
'',
|
|
347
|
+
section('Next step'),
|
|
348
|
+
quote(nextStep || 'This task is now cancelled. If you still want to reach this same target, ask me to retry later.'),
|
|
349
|
+
'',
|
|
350
|
+
'---',
|
|
351
|
+
'',
|
|
352
|
+
'I did not change the target or send this message to anyone else.'
|
|
353
|
+
].join('\n')
|
|
354
|
+
return {
|
|
355
|
+
title,
|
|
356
|
+
summary: `${clean(targetAgentId) || 'The remote agent'} could not be reached through AgentSquared.`,
|
|
357
|
+
message
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
export function buildReceiverBaseReport({
|
|
362
|
+
localAgentId,
|
|
363
|
+
remoteAgentId,
|
|
364
|
+
incomingSkillHint = '',
|
|
365
|
+
selectedSkill,
|
|
366
|
+
conversationKey = '',
|
|
367
|
+
receivedAt,
|
|
368
|
+
inboundText,
|
|
369
|
+
peerReplyText,
|
|
370
|
+
repliedAt,
|
|
371
|
+
skillSummary = '',
|
|
372
|
+
conversationTurns = 1,
|
|
373
|
+
stopReason = '',
|
|
374
|
+
turnOutline = [],
|
|
375
|
+
detailsAvailableInInbox = false,
|
|
376
|
+
remoteSentAt = '',
|
|
377
|
+
language = 'en',
|
|
378
|
+
timeZone = '',
|
|
379
|
+
localTime = false
|
|
380
|
+
} = {}) {
|
|
381
|
+
const title = `**${logo(language)} New AgentSquared message from ${clean(remoteAgentId) || 'a remote agent'}**`
|
|
382
|
+
const normalizedTurnOutline = ensureTurnOutline(turnOutline, conversationTurns, {
|
|
383
|
+
language,
|
|
384
|
+
fallbackSummary: 'Received the remote message and completed this turn.'
|
|
385
|
+
})
|
|
386
|
+
const message = [
|
|
387
|
+
section('Conversation result'),
|
|
388
|
+
`- Sender: ${clean(remoteAgentId) || 'unknown'}`,
|
|
389
|
+
`- Recipient: ${clean(localAgentId) || 'unknown'}`,
|
|
390
|
+
`- Received At${localTime ? ' (Local Time)' : ' (UTC)'}: ${formatDisplayTime(receivedAt, { language, timeZone, localTime })}`,
|
|
391
|
+
...(clean(remoteSentAt) ? [`- Remote Sent At${localTime ? ' (Local Time)' : ' (UTC)'}: ${formatDisplayTime(remoteSentAt, { language, timeZone, localTime })}`] : []),
|
|
392
|
+
...(clean(conversationKey) ? [`- Conversation Key: ${clean(conversationKey)}`] : []),
|
|
393
|
+
`- Incoming Skill Hint: ${clean(incomingSkillHint) || 'friend-im'}`,
|
|
394
|
+
`- Local Skill Used: ${clean(selectedSkill) || 'friend-im'}`,
|
|
395
|
+
'',
|
|
396
|
+
section('Overall summary'),
|
|
397
|
+
...(clean(skillSummary) ? [quote(skillSummary)] : ['> This conversation completed.']),
|
|
398
|
+
'',
|
|
399
|
+
section('Detailed conversation'),
|
|
400
|
+
...normalizedTurnOutline.map((item) => `- Turn ${item.turnIndex}: ${item.summary}`),
|
|
401
|
+
'',
|
|
402
|
+
section('Actions taken'),
|
|
403
|
+
`- Reviewed the inbound AgentSquared message from ${clean(remoteAgentId) || 'the remote agent'}.`,
|
|
404
|
+
`- Replied to the remote agent at ${formatDisplayTime(repliedAt, { language, timeZone, localTime })}.`,
|
|
405
|
+
`- Total turns: ${conversationTurns}.`,
|
|
406
|
+
...(clean(stopReason) ? [`- Stopped with reason: ${clean(stopReason)}.`] : []),
|
|
407
|
+
'',
|
|
408
|
+
'---',
|
|
409
|
+
'',
|
|
410
|
+
...(detailsAvailableInInbox ? ['If you need the turn-by-turn details, check the local AgentSquared inbox.', ''] : []),
|
|
411
|
+
'If my reply needs correction, tell me and I can adjust future exchanges accordingly.'
|
|
412
|
+
].join('\n')
|
|
413
|
+
return {
|
|
414
|
+
title,
|
|
415
|
+
summary: `${clean(remoteAgentId) || 'A remote agent'} completed a conversation with me.`,
|
|
416
|
+
message,
|
|
417
|
+
skillSummary: clean(skillSummary)
|
|
418
|
+
}
|
|
419
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { requestJson } from '../transport/http_json.mjs'
|
|
2
|
+
|
|
3
|
+
const DEFAULT_GATEWAY_BASE = 'http://127.0.0.1:46357'
|
|
4
|
+
|
|
5
|
+
async function gatewayGet(gatewayBase, path) {
|
|
6
|
+
return requestJson(`${gatewayBase}${path}`, {
|
|
7
|
+
method: 'GET'
|
|
8
|
+
})
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
async function gatewayPost(gatewayBase, path, payload) {
|
|
12
|
+
return requestJson(`${gatewayBase}${path}`, {
|
|
13
|
+
method: 'POST',
|
|
14
|
+
payload
|
|
15
|
+
})
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export async function gatewayHealth(gatewayBase = DEFAULT_GATEWAY_BASE) {
|
|
19
|
+
return gatewayGet(gatewayBase, '/health')
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export async function gatewayConnect(gatewayBase = DEFAULT_GATEWAY_BASE, payload) {
|
|
23
|
+
return gatewayPost(gatewayBase, '/connect', payload)
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export async function gatewayInboxIndex(gatewayBase = DEFAULT_GATEWAY_BASE) {
|
|
27
|
+
return gatewayGet(gatewayBase, '/inbox/index')
|
|
28
|
+
}
|