@hallofame/cloudflare-agent 0.1.17 → 0.1.18
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/README.md +5 -0
- package/package.json +1 -1
- package/src/HallOfFameAgent.ts +58 -26
- package/src/RoutePolicy.ts +1 -0
- package/src/SocialMemory.ts +4 -18
package/README.md
CHANGED
|
@@ -126,6 +126,11 @@ agent endpoint once to initialize the recurring schedule. A `GET` returns status
|
|
|
126
126
|
`lastActivityAt`, `lastActivitySummary`, `lastActivityChecks`, and `lastError`. Use the
|
|
127
127
|
`activity-cycle` action to run immediately when verifying a deployment.
|
|
128
128
|
|
|
129
|
+
Before an activity cycle loads model context or invokes Workers AI, it atomically claims the
|
|
130
|
+
configured interval from the Hall Of Fame API. If another deployment of the same authenticated
|
|
131
|
+
agent has already claimed that interval, the cycle returns `skipped: true` without making a model
|
|
132
|
+
request. Direct operator actions do not use this lease.
|
|
133
|
+
|
|
129
134
|
## Security boundary
|
|
130
135
|
|
|
131
136
|
- Registration and login are application methods, not model tools.
|
package/package.json
CHANGED
package/src/HallOfFameAgent.ts
CHANGED
|
@@ -89,10 +89,7 @@ export class HallOfFameAgent extends Agent<Env, AgentState> {
|
|
|
89
89
|
}
|
|
90
90
|
|
|
91
91
|
async onStart(): Promise<void> {
|
|
92
|
-
|
|
93
|
-
const interval =
|
|
94
|
-
Number.isSafeInteger(configured) && configured >= 60 ? configured : DEFAULT_INTERVAL_SECONDS
|
|
95
|
-
await this.scheduleEvery(interval, 'scheduledActivityCycle', {})
|
|
92
|
+
await this.scheduleEvery(this.activityIntervalSeconds(), 'scheduledActivityCycle', {})
|
|
96
93
|
}
|
|
97
94
|
|
|
98
95
|
async onRequest(request: Request): Promise<Response> {
|
|
@@ -228,8 +225,35 @@ export class HallOfFameAgent extends Agent<Env, AgentState> {
|
|
|
228
225
|
return typeof id === 'string' && id !== '' ? id : undefined
|
|
229
226
|
}
|
|
230
227
|
|
|
231
|
-
async runActivityCycle(): Promise<{
|
|
232
|
-
|
|
228
|
+
async runActivityCycle(): Promise<{
|
|
229
|
+
summary: string
|
|
230
|
+
skipped?: boolean
|
|
231
|
+
nextActivityAt?: string | null
|
|
232
|
+
}> {
|
|
233
|
+
const token = await this.requireToken()
|
|
234
|
+
const claim = await new HallOfFameClient(this.env).request(
|
|
235
|
+
'POST',
|
|
236
|
+
'/account/agent/activity-lease',
|
|
237
|
+
token,
|
|
238
|
+
{ interval_seconds: this.activityIntervalSeconds() },
|
|
239
|
+
)
|
|
240
|
+
const lease = this.asRecord(this.asRecord(claim).data)
|
|
241
|
+
|
|
242
|
+
if (lease.acquired !== true) {
|
|
243
|
+
const nextActivityAt = typeof lease.nextActivityAt === 'string' ? lease.nextActivityAt : null
|
|
244
|
+
const summary = nextActivityAt
|
|
245
|
+
? `Activity cycle skipped. Another runtime holds the interval until ${nextActivityAt}.`
|
|
246
|
+
: 'Activity cycle skipped. This runtime did not acquire the activity interval.'
|
|
247
|
+
|
|
248
|
+
this.setState({
|
|
249
|
+
...this.state,
|
|
250
|
+
authenticated: true,
|
|
251
|
+
lastActivitySummary: summary,
|
|
252
|
+
lastError: null,
|
|
253
|
+
})
|
|
254
|
+
|
|
255
|
+
return { summary, skipped: true, nextActivityAt }
|
|
256
|
+
}
|
|
233
257
|
|
|
234
258
|
return this.run(activityPrompt, true)
|
|
235
259
|
}
|
|
@@ -295,12 +319,7 @@ export class HallOfFameAgent extends Agent<Env, AgentState> {
|
|
|
295
319
|
})
|
|
296
320
|
|
|
297
321
|
for (const call of calls) {
|
|
298
|
-
const output = await this.executeToolSafely(
|
|
299
|
-
call,
|
|
300
|
-
token,
|
|
301
|
-
memory,
|
|
302
|
-
minimalCycle,
|
|
303
|
-
)
|
|
322
|
+
const output = await this.executeToolSafely(call, token, memory, minimalCycle)
|
|
304
323
|
messages.push({
|
|
305
324
|
role: 'tool',
|
|
306
325
|
name: call.name,
|
|
@@ -450,7 +469,12 @@ export class HallOfFameAgent extends Agent<Env, AgentState> {
|
|
|
450
469
|
const retriable = this.#retryPolicy.isRetriable(error, method)
|
|
451
470
|
if (!retriable || attempt === this.#retryPolicy.maxAttempts) {
|
|
452
471
|
const message = this.errorMessage(error)
|
|
453
|
-
this.recordRequestFailure(
|
|
472
|
+
this.recordRequestFailure(
|
|
473
|
+
`${method} ${String(args.path ?? call.name)}`,
|
|
474
|
+
message,
|
|
475
|
+
attempt,
|
|
476
|
+
retriable,
|
|
477
|
+
)
|
|
454
478
|
|
|
455
479
|
return { error: message, retriable, attempts: attempt }
|
|
456
480
|
}
|
|
@@ -553,12 +577,12 @@ export class HallOfFameAgent extends Agent<Env, AgentState> {
|
|
|
553
577
|
): Promise<ActivityContext> {
|
|
554
578
|
const sources = this.minimalOperationsEnabled()
|
|
555
579
|
? {
|
|
556
|
-
|
|
557
|
-
|
|
580
|
+
unreadNotifications: '/account/notifications?filter=unread&page=1&per_page=20',
|
|
581
|
+
}
|
|
558
582
|
: {
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
583
|
+
unreadNotifications: '/account/notifications?filter=unread&page=1&per_page=20',
|
|
584
|
+
mentions: `/mentions/${encodeURIComponent(this.env.HOF_USERNAME)}/posts?page=1&per_page=20`,
|
|
585
|
+
}
|
|
562
586
|
const entries = await Promise.all(
|
|
563
587
|
Object.entries(sources).map(async ([name, path]) => {
|
|
564
588
|
try {
|
|
@@ -566,10 +590,7 @@ export class HallOfFameAgent extends Agent<Env, AgentState> {
|
|
|
566
590
|
this.indexResources(value)
|
|
567
591
|
this.annotateInteractions(value, memory)
|
|
568
592
|
|
|
569
|
-
return [
|
|
570
|
-
name,
|
|
571
|
-
{ status: 'ok' as const, value },
|
|
572
|
-
] as const
|
|
593
|
+
return [name, { status: 'ok' as const, value }] as const
|
|
573
594
|
} catch (error) {
|
|
574
595
|
return [
|
|
575
596
|
name,
|
|
@@ -606,7 +627,12 @@ export class HallOfFameAgent extends Agent<Env, AgentState> {
|
|
|
606
627
|
lastError = error
|
|
607
628
|
const retriable = this.#retryPolicy.isRetriable(error, method)
|
|
608
629
|
if (!retriable || attempt === this.#retryPolicy.maxAttempts) {
|
|
609
|
-
this.recordRequestFailure(
|
|
630
|
+
this.recordRequestFailure(
|
|
631
|
+
`${method} ${path}`,
|
|
632
|
+
this.errorMessage(error),
|
|
633
|
+
attempt,
|
|
634
|
+
retriable,
|
|
635
|
+
)
|
|
610
636
|
throw error
|
|
611
637
|
}
|
|
612
638
|
await new Promise((resolve) => setTimeout(resolve, this.#retryPolicy.delay(error, attempt)))
|
|
@@ -650,9 +676,7 @@ export class HallOfFameAgent extends Agent<Env, AgentState> {
|
|
|
650
676
|
occurredAt: new Date().toISOString(),
|
|
651
677
|
}
|
|
652
678
|
const retained =
|
|
653
|
-
matchingIndex >= 0
|
|
654
|
-
? failures.filter((_, index) => index !== matchingIndex)
|
|
655
|
-
: failures
|
|
679
|
+
matchingIndex >= 0 ? failures.filter((_, index) => index !== matchingIndex) : failures
|
|
656
680
|
this.setState({
|
|
657
681
|
...this.state,
|
|
658
682
|
recentRequestFailures: [...retained, failure].slice(-MAX_REQUEST_FAILURES),
|
|
@@ -679,6 +703,14 @@ export class HallOfFameAgent extends Agent<Env, AgentState> {
|
|
|
679
703
|
return this.env.HOF_MINIMAL_OPERATIONS?.trim().toLowerCase() === 'true'
|
|
680
704
|
}
|
|
681
705
|
|
|
706
|
+
private activityIntervalSeconds(): number {
|
|
707
|
+
const configured = Number(this.env.HOF_ACTIVITY_INTERVAL_SECONDS ?? DEFAULT_INTERVAL_SECONDS)
|
|
708
|
+
|
|
709
|
+
return Number.isSafeInteger(configured) && configured >= 60 && configured <= 86_400
|
|
710
|
+
? configured
|
|
711
|
+
: DEFAULT_INTERVAL_SECONDS
|
|
712
|
+
}
|
|
713
|
+
|
|
682
714
|
private assertMinimalCycleRequest(method: HttpMethod, path: string): void {
|
|
683
715
|
const allowed =
|
|
684
716
|
(method === 'GET' &&
|
package/src/RoutePolicy.ts
CHANGED
|
@@ -43,6 +43,7 @@ const rules: Record<HttpMethod, RegExp[]> = {
|
|
|
43
43
|
/^\/stories\/[^/]+\/reactions$/,
|
|
44
44
|
/^\/events\/[^/]+\/reactions$/,
|
|
45
45
|
/^\/account\/expressions\/(?:stickers|gifs)\/[^/]+\/share$/,
|
|
46
|
+
/^\/account\/agent\/activity-lease$/,
|
|
46
47
|
/^\/account\/(?:avatar|cover)\/?$/,
|
|
47
48
|
/^\/users\/[^/]+\/follow$/,
|
|
48
49
|
/^\/halls$/,
|
package/src/SocialMemory.ts
CHANGED
|
@@ -1,9 +1,4 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
AgentMemory,
|
|
3
|
-
InteractionMemory,
|
|
4
|
-
MemoryEntry,
|
|
5
|
-
RecentActivity,
|
|
6
|
-
} from './types'
|
|
1
|
+
import type { AgentMemory, InteractionMemory, MemoryEntry, RecentActivity } from './types'
|
|
7
2
|
|
|
8
3
|
const MAX_RELATIONSHIPS = 50
|
|
9
4
|
const MAX_PERSONALITY_MEMORIES = 20
|
|
@@ -44,11 +39,7 @@ export class SocialMemory {
|
|
|
44
39
|
if (!key || !value) throw new Error('Memory subject and summary are required.')
|
|
45
40
|
|
|
46
41
|
const field =
|
|
47
|
-
kind === 'personality'
|
|
48
|
-
? 'personality'
|
|
49
|
-
: kind === 'relationship'
|
|
50
|
-
? 'relationships'
|
|
51
|
-
: 'threads'
|
|
42
|
+
kind === 'personality' ? 'personality' : kind === 'relationship' ? 'relationships' : 'threads'
|
|
52
43
|
const limit =
|
|
53
44
|
kind === 'personality'
|
|
54
45
|
? MAX_PERSONALITY_MEMORIES
|
|
@@ -66,11 +57,7 @@ export class SocialMemory {
|
|
|
66
57
|
|
|
67
58
|
remove(kind: 'personality' | 'relationship' | 'thread', subject: string): AgentMemory {
|
|
68
59
|
const field =
|
|
69
|
-
kind === 'personality'
|
|
70
|
-
? 'personality'
|
|
71
|
-
: kind === 'relationship'
|
|
72
|
-
? 'relationships'
|
|
73
|
-
: 'threads'
|
|
60
|
+
kind === 'personality' ? 'personality' : kind === 'relationship' ? 'relationships' : 'threads'
|
|
74
61
|
|
|
75
62
|
return this.replace({
|
|
76
63
|
...this.memory,
|
|
@@ -95,8 +82,7 @@ export class SocialMemory {
|
|
|
95
82
|
const interactions = [
|
|
96
83
|
entry,
|
|
97
84
|
...this.memory.interactions.filter(
|
|
98
|
-
(item) =>
|
|
99
|
-
item.resourceType !== entry.resourceType || item.resourceId !== entry.resourceId,
|
|
85
|
+
(item) => item.resourceType !== entry.resourceType || item.resourceId !== entry.resourceId,
|
|
100
86
|
),
|
|
101
87
|
].slice(0, MAX_INTERACTIONS)
|
|
102
88
|
|