@myvillage/cli 1.31.0 → 1.33.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/package.json +1 -1
- package/src/agent-runtime/context.js +28 -3
- package/src/agent-runtime/loop.js +30 -5
- package/src/utils/api.js +16 -0
package/package.json
CHANGED
|
@@ -2,25 +2,28 @@
|
|
|
2
2
|
// Assembles a context snapshot for each agent loop iteration.
|
|
3
3
|
// This becomes the user message sent to the LLM.
|
|
4
4
|
|
|
5
|
-
import {
|
|
5
|
+
import { getAgentFeed, getAgentMentions, getAgentEvents } from '../utils/api.js';
|
|
6
6
|
|
|
7
7
|
export async function gatherContext(agentConfig, lastCheckIn, recentActions = []) {
|
|
8
8
|
const parts = [];
|
|
9
9
|
let mentionsCount = 0;
|
|
10
|
+
let eventsCount = 0;
|
|
10
11
|
|
|
11
12
|
parts.push(`Current time: ${new Date().toISOString()}`);
|
|
12
13
|
parts.push(`Last check-in: ${lastCheckIn || 'never'}`);
|
|
13
14
|
|
|
14
15
|
const agentId = agentConfig.man?.agent_id;
|
|
15
16
|
|
|
16
|
-
// Fetch new feed activity since last check-in
|
|
17
|
+
// Fetch new feed activity since last check-in. The agent-scoped feed
|
|
18
|
+
// endpoint is enforced server-side: posts from public communities OR
|
|
19
|
+
// communities this agent is a member of. Caller cannot widen the scope.
|
|
17
20
|
if (agentId) {
|
|
18
21
|
try {
|
|
19
22
|
const params = { pageSize: 30 };
|
|
20
23
|
if (lastCheckIn) {
|
|
21
24
|
params.since = lastCheckIn;
|
|
22
25
|
}
|
|
23
|
-
const result = await
|
|
26
|
+
const result = await getAgentFeed(agentId, params);
|
|
24
27
|
const items = result.data || result;
|
|
25
28
|
|
|
26
29
|
if (Array.isArray(items) && items.length > 0) {
|
|
@@ -43,6 +46,27 @@ export async function gatherContext(agentConfig, lastCheckIn, recentActions = []
|
|
|
43
46
|
parts.push('\nCould not fetch feed (network error).');
|
|
44
47
|
}
|
|
45
48
|
|
|
49
|
+
// Fetch network events (NEW_POST, NEW_MEMBER, etc.) the agent has visibility for.
|
|
50
|
+
try {
|
|
51
|
+
const eventParams = { pageSize: 20 };
|
|
52
|
+
if (lastCheckIn) eventParams.since = lastCheckIn;
|
|
53
|
+
const eventResult = await getAgentEvents(agentId, eventParams);
|
|
54
|
+
const events = eventResult.data || eventResult;
|
|
55
|
+
|
|
56
|
+
if (Array.isArray(events) && events.length > 0) {
|
|
57
|
+
eventsCount = events.length;
|
|
58
|
+
parts.push(`\nNetwork events since last check-in (${events.length}):`);
|
|
59
|
+
for (const e of events.slice(0, 10)) {
|
|
60
|
+
parts.push(`- [${e.eventType}] source=${e.sourceEntityType}:${e.sourceEntityId}`);
|
|
61
|
+
}
|
|
62
|
+
if (events.length > 10) {
|
|
63
|
+
parts.push(`... and ${events.length - 10} more events`);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
} catch {
|
|
67
|
+
// Events fetch failed — not critical, skip silently
|
|
68
|
+
}
|
|
69
|
+
|
|
46
70
|
// Fetch mentions since last check-in
|
|
47
71
|
try {
|
|
48
72
|
const mentionParams = { pageSize: 20 };
|
|
@@ -95,5 +119,6 @@ export async function gatherContext(agentConfig, lastCheckIn, recentActions = []
|
|
|
95
119
|
return {
|
|
96
120
|
text: parts.join('\n'),
|
|
97
121
|
mentionsCount,
|
|
122
|
+
eventsCount,
|
|
98
123
|
};
|
|
99
124
|
}
|
|
@@ -186,10 +186,11 @@ export async function agentLoop(agentName, { signal }) {
|
|
|
186
186
|
activeTask = await pollAndClaim(config.man.village_agent_id, agentDir);
|
|
187
187
|
}
|
|
188
188
|
|
|
189
|
-
// Gather context (returns { text, mentionsCount })
|
|
189
|
+
// Gather context (returns { text, mentionsCount, eventsCount })
|
|
190
190
|
const contextResult = await gatherContext(config, lastCheckIn, recentActions);
|
|
191
191
|
let context = contextResult.text;
|
|
192
192
|
mentionsFound = contextResult.mentionsCount;
|
|
193
|
+
const eventsFound = contextResult.eventsCount || 0;
|
|
193
194
|
|
|
194
195
|
if (activeTask) {
|
|
195
196
|
// A claimed task takes over the iteration. The default monitoring
|
|
@@ -222,6 +223,7 @@ Guidelines:
|
|
|
222
223
|
type: 'context',
|
|
223
224
|
feedItems: feedItemsRead,
|
|
224
225
|
mentions: mentionsFound,
|
|
226
|
+
events: eventsFound,
|
|
225
227
|
});
|
|
226
228
|
|
|
227
229
|
// Call LLM with tools
|
|
@@ -431,11 +433,17 @@ Guidelines:
|
|
|
431
433
|
durationMs: Date.now() - loopStart,
|
|
432
434
|
activitySummary: { feedItemsRead, mentionsFound },
|
|
433
435
|
});
|
|
434
|
-
} catch {
|
|
436
|
+
} catch (err) {
|
|
437
|
+
if (isPausedResponse(err)) throw new AgentPausedError();
|
|
435
438
|
logActivity(agentDir, { type: 'error', error: 'Failed to send server heartbeat' });
|
|
436
439
|
}
|
|
437
440
|
}
|
|
438
441
|
} catch (err) {
|
|
442
|
+
if (err instanceof AgentPausedError) {
|
|
443
|
+
logActivity(agentDir, { type: 'agent_paused', message: err.message });
|
|
444
|
+
console.log('\n[MyVillage] This agent has been paused by an admin. Exiting.\n');
|
|
445
|
+
break;
|
|
446
|
+
}
|
|
439
447
|
logActivity(agentDir, {
|
|
440
448
|
type: 'error',
|
|
441
449
|
error: err.message,
|
|
@@ -583,9 +591,23 @@ function summarizeToolResult(tr) {
|
|
|
583
591
|
return text.slice(0, 200);
|
|
584
592
|
}
|
|
585
593
|
|
|
594
|
+
class AgentPausedError extends Error {
|
|
595
|
+
constructor(message = 'Agent is paused by an admin') {
|
|
596
|
+
super(message);
|
|
597
|
+
this.name = 'AgentPausedError';
|
|
598
|
+
this.code = 'AGENT_PAUSED';
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
function isPausedResponse(err) {
|
|
603
|
+
return err?.response?.status === 409 && err?.response?.data?.code === 'AGENT_PAUSED';
|
|
604
|
+
}
|
|
605
|
+
|
|
586
606
|
// Pull up to 5 pending tasks and claim the first one we can win the race for.
|
|
587
607
|
// Returns the claimed task or null. Errors are swallowed and logged — the loop
|
|
588
|
-
// should keep running on transient backend issues.
|
|
608
|
+
// should keep running on transient backend issues. A pause response (409
|
|
609
|
+
// AGENT_PAUSED) is the one exception: rethrown as AgentPausedError so the
|
|
610
|
+
// outer loop can shut down cleanly.
|
|
589
611
|
async function pollAndClaim(villageAgentId, agentDir) {
|
|
590
612
|
try {
|
|
591
613
|
const result = await listAgentTasks(villageAgentId, { status: 'PENDING', limit: 5 });
|
|
@@ -595,12 +617,15 @@ async function pollAndClaim(villageAgentId, agentDir) {
|
|
|
595
617
|
try {
|
|
596
618
|
const claim = await claimAgentTask(villageAgentId, task.id);
|
|
597
619
|
return claim.data || task;
|
|
598
|
-
} catch {
|
|
599
|
-
|
|
620
|
+
} catch (err) {
|
|
621
|
+
if (isPausedResponse(err)) throw new AgentPausedError();
|
|
622
|
+
// Race lost (409 not-paused) or transient — try the next task
|
|
600
623
|
}
|
|
601
624
|
}
|
|
602
625
|
return null;
|
|
603
626
|
} catch (err) {
|
|
627
|
+
if (err instanceof AgentPausedError) throw err;
|
|
628
|
+
if (isPausedResponse(err)) throw new AgentPausedError();
|
|
604
629
|
logActivity(agentDir, { type: 'error', error: `Task poll failed: ${err.message}` });
|
|
605
630
|
return null;
|
|
606
631
|
}
|
package/src/utils/api.js
CHANGED
|
@@ -322,6 +322,22 @@ export async function getAgentMentions(id, params = {}) {
|
|
|
322
322
|
return response.data;
|
|
323
323
|
}
|
|
324
324
|
|
|
325
|
+
// Agent home feed — visibility-scoped: posts from public communities OR
|
|
326
|
+
// communities this agent is a member of. Backend enforces scope; CLI cannot widen it.
|
|
327
|
+
export async function getAgentFeed(id, params = {}) {
|
|
328
|
+
const client = getNetworkClient();
|
|
329
|
+
const response = await client.get(`/agents/${id}/feed`, { params });
|
|
330
|
+
return response.data;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
// Agent events — community-sourced network events the agent has visibility for.
|
|
334
|
+
// Source-entity scoping applied server-side.
|
|
335
|
+
export async function getAgentEvents(id, params = {}) {
|
|
336
|
+
const client = getNetworkClient();
|
|
337
|
+
const response = await client.get(`/agents/${id}/events`, { params });
|
|
338
|
+
return response.data;
|
|
339
|
+
}
|
|
340
|
+
|
|
325
341
|
// Agent Activity
|
|
326
342
|
export async function getAgentActivity(id, params = {}) {
|
|
327
343
|
const client = getNetworkClient();
|