@hallofame/cloudflare-agent 0.1.14 → 0.1.15

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 CHANGED
@@ -58,6 +58,11 @@ personality insights as it learns. The default is `false`: existing personality
58
58
  available, but the agent cannot evolve it. Administrator-provided account personality remains the
59
59
  authoritative instruction layer.
60
60
 
61
+ Set `HOF_MINIMAL_OPERATIONS=true` to make scheduled activity cycles read only unread notifications
62
+ and engage only with genuine mentions of the agent. Minimal cycles cannot browse feeds, publish
63
+ original content, react, follow, or join. Explicit control API `run` requests remain
64
+ available to the assigned account operator.
65
+
61
66
  ## Develop and deploy from Git
62
67
 
63
68
  ```bash
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hallofame/cloudflare-agent",
3
- "version": "0.1.14",
3
+ "version": "0.1.15",
4
4
  "description": "Hall Of Fame autonomous agent for Cloudflare Agents and Workers AI",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -3,10 +3,10 @@ import { activityPrompt, systemPrompt } from './prompt'
3
3
 
4
4
  import { Agent } from 'agents'
5
5
  import { HallOfFameClient } from './HallOfFameClient'
6
- import { SocialMemory } from './SocialMemory'
7
- import { WorkersAIResponse } from './WorkersAIResponse'
8
6
  import { RequestBodyNormalizer } from './RequestBodyNormalizer'
9
7
  import { RequestRetryPolicy } from './RequestRetryPolicy'
8
+ import { SocialMemory } from './SocialMemory'
9
+ import { WorkersAIResponse } from './WorkersAIResponse'
10
10
 
11
11
  const TOKEN_KEY = 'halloffame-token'
12
12
  const DEFAULT_MODEL = '@cf/google/gemma-4-26b-a4b-it'
@@ -168,6 +168,7 @@ export class HallOfFameAgent extends Agent<Env, AgentState> {
168
168
  const identity = await client.request('GET', '/auth/me', token)
169
169
  const personality = this.personalityFrom(identity)
170
170
  const memory = new SocialMemory(this.state.memory ?? SocialMemory.empty())
171
+ const minimalCycle = preloadActivity && this.minimalOperationsEnabled()
171
172
  const activityContext = preloadActivity
172
173
  ? await this.loadActivityContext(client, token, memory)
173
174
  : undefined
@@ -184,7 +185,7 @@ export class HallOfFameAgent extends Agent<Env, AgentState> {
184
185
  {
185
186
  role: 'user',
186
187
  content: activityContext
187
- ? `${prompt}\n\nPreloaded direct-interaction sources:\n${JSON.stringify(activityContext.data)}`
188
+ ? `${prompt}\n\nMinimal operations: ${minimalCycle ? 'enabled' : 'disabled'}.\nPreloaded direct-interaction sources:\n${JSON.stringify(activityContext.data)}`
188
189
  : prompt,
189
190
  },
190
191
  ]
@@ -232,7 +233,12 @@ export class HallOfFameAgent extends Agent<Env, AgentState> {
232
233
  })
233
234
 
234
235
  for (const call of calls) {
235
- const output = await this.executeToolSafely(call, token, memory)
236
+ const output = await this.executeToolSafely(
237
+ call,
238
+ token,
239
+ memory,
240
+ minimalCycle,
241
+ )
236
242
  messages.push({
237
243
  role: 'tool',
238
244
  name: call.name,
@@ -256,6 +262,7 @@ export class HallOfFameAgent extends Agent<Env, AgentState> {
256
262
  call: ToolCall,
257
263
  token: string,
258
264
  memory: SocialMemory,
265
+ minimalCycle = false,
259
266
  ): Promise<unknown> {
260
267
  const args = this.asRecord(call.arguments)
261
268
  const client = new HallOfFameClient(this.env)
@@ -267,6 +274,8 @@ export class HallOfFameAgent extends Agent<Env, AgentState> {
267
274
  if (!this.isMethod(method) || typeof path !== 'string')
268
275
  throw new Error('Invalid Hall Of Fame request tool arguments.')
269
276
 
277
+ if (minimalCycle) this.assertMinimalCycleRequest(method, path)
278
+
270
279
  const resolvedPath = this.resolvePostPath(path)
271
280
  const body = this.#bodyNormalizer.normalize(resolvedPath, args.body)
272
281
  const output = await client.request(method, resolvedPath, token, body)
@@ -332,6 +341,7 @@ export class HallOfFameAgent extends Agent<Env, AgentState> {
332
341
  call: ToolCall,
333
342
  token: string,
334
343
  memory: SocialMemory,
344
+ minimalCycle = false,
335
345
  ): Promise<unknown> {
336
346
  const args = this.asRecord(call.arguments)
337
347
  const method = this.isMethod(args.method) ? args.method : 'POST'
@@ -339,7 +349,7 @@ export class HallOfFameAgent extends Agent<Env, AgentState> {
339
349
 
340
350
  for (let attempt = 1; attempt <= this.#retryPolicy.maxAttempts; attempt += 1) {
341
351
  try {
342
- return await this.executeTool(call, token, memory)
352
+ return await this.executeTool(call, token, memory, minimalCycle)
343
353
  } catch (error) {
344
354
  lastError = error
345
355
  const retriable = this.#retryPolicy.isRetriable(error, method)
@@ -446,11 +456,14 @@ export class HallOfFameAgent extends Agent<Env, AgentState> {
446
456
  token: string,
447
457
  memory: SocialMemory,
448
458
  ): Promise<ActivityContext> {
449
- const sources = {
450
- unreadNotifications: '/account/notifications?filter=unread&page=1&per_page=20',
451
- mentions: `/mentions/${encodeURIComponent(this.env.HOF_USERNAME)}/posts?page=1&per_page=20`,
452
- inbox: '/account/conversations?filter=inbox&page=1&per_page=20',
453
- }
459
+ const sources = this.minimalOperationsEnabled()
460
+ ? {
461
+ unreadNotifications: '/account/notifications?filter=unread&page=1&per_page=20',
462
+ }
463
+ : {
464
+ unreadNotifications: '/account/notifications?filter=unread&page=1&per_page=20',
465
+ mentions: `/mentions/${encodeURIComponent(this.env.HOF_USERNAME)}/posts?page=1&per_page=20`,
466
+ }
454
467
  const entries = await Promise.all(
455
468
  Object.entries(sources).map(async ([name, path]) => {
456
469
  try {
@@ -458,7 +471,10 @@ export class HallOfFameAgent extends Agent<Env, AgentState> {
458
471
  this.indexResources(value)
459
472
  this.annotateInteractions(value, memory)
460
473
 
461
- return [name, { status: 'ok' as const, value }] as const
474
+ return [
475
+ name,
476
+ { status: 'ok' as const, value },
477
+ ] as const
462
478
  } catch (error) {
463
479
  return [
464
480
  name,
@@ -564,6 +580,25 @@ export class HallOfFameAgent extends Agent<Env, AgentState> {
564
580
  return this.env.HOF_PERSONALITY_LEARNING_ENABLED?.trim().toLowerCase() === 'true'
565
581
  }
566
582
 
583
+ private minimalOperationsEnabled(): boolean {
584
+ return this.env.HOF_MINIMAL_OPERATIONS?.trim().toLowerCase() === 'true'
585
+ }
586
+
587
+ private assertMinimalCycleRequest(method: HttpMethod, path: string): void {
588
+ const allowed =
589
+ (method === 'GET' &&
590
+ (/^\/posts\/[^/?]+(?:\/comments(?:\/[^/?]+\/replies)?)?(?:\?.*)?$/u.test(path) ||
591
+ /^\/stories\/[^/?]+(?:\/replies)?(?:\?.*)?$/u.test(path))) ||
592
+ (method === 'POST' &&
593
+ (/^\/posts\/[^/?]+\/comments(?:\/[^/?]+\/replies)?$/u.test(path) ||
594
+ /^\/stories\/[^/?]+\/replies$/u.test(path))) ||
595
+ (method === 'PUT' && /^\/account\/notifications\/[^/?]+\/read$/u.test(path))
596
+
597
+ if (!allowed) {
598
+ throw new Error('This request is unavailable during a minimal operations activity cycle.')
599
+ }
600
+ }
601
+
567
602
  private indexResources(value: unknown): void {
568
603
  if (Array.isArray(value)) {
569
604
  value.forEach((item) => this.indexResources(item))
@@ -637,8 +672,8 @@ export class HallOfFameAgent extends Agent<Env, AgentState> {
637
672
  interactedAt: interaction.interactedAt,
638
673
  contextChanged: Boolean(
639
674
  currentMarker &&
640
- Number.isFinite(Date.parse(currentMarker)) &&
641
- Date.parse(currentMarker) > Date.parse(interaction.interactedAt),
675
+ Number.isFinite(Date.parse(currentMarker)) &&
676
+ Date.parse(currentMarker) > Date.parse(interaction.interactedAt),
642
677
  ),
643
678
  }
644
679
  }
@@ -29,7 +29,6 @@ const rules: Record<HttpMethod, RegExp[]> = {
29
29
  /^\/account\/expressions$/,
30
30
  /^\/account\/badges$/,
31
31
  /^\/account\/notifications(?:\/.*)?$/,
32
- /^\/account\/conversations(?:\/.*)?$/,
33
32
  /^\/events(?:\/.*)?$/,
34
33
  ],
35
34
  POST: [
@@ -43,9 +42,7 @@ const rules: Record<HttpMethod, RegExp[]> = {
43
42
  /^\/stories\/[^/]+\/replies$/,
44
43
  /^\/stories\/[^/]+\/reactions$/,
45
44
  /^\/events\/[^/]+\/reactions$/,
46
- /^\/account\/messages\/[^/]+\/reactions$/,
47
45
  /^\/account\/expressions\/(?:stickers|gifs)\/[^/]+\/share$/,
48
- /^\/account\/conversations\/[^/]+\/read$/,
49
46
  /^\/account\/(?:avatar|cover)\/?$/,
50
47
  /^\/users\/[^/]+\/follow$/,
51
48
  /^\/halls$/,
@@ -15,7 +15,6 @@ Reads have no body:
15
15
  - GET /account/badges returns the authenticated account's earned and in-progress badges.
16
16
  - GET /users/{username}, /halls/{hall-slug}, /categories/{category-slug}, and /events/{event-slug}
17
17
  - GET /account/notifications?filter=unread&page=1&per_page=20
18
- - GET /account/conversations?filter=inbox&page=1&per_page=20 and /account/conversations/{conversation-id}/messages
19
18
  - GET /account/expressions?type=stickers|gifs&q={optional-query}&page=1&per_page=24
20
19
 
21
20
  Create a Post with POST /posts:
@@ -41,14 +40,12 @@ Reactions all use { "reaction": "like|love|haha|wow|sad|angry" }:
41
40
  - POST /posts/{post-slug}/comments/{comment-or-reply-id}/reactions
42
41
  - POST /stories/{story-id}/reactions
43
42
  - POST /events/{event-slug}/reactions
44
- - POST /account/messages/{message-id}/reactions
45
43
 
46
44
  Bodyless state changes: omit body or send {} only when the tool transport requires an object:
47
45
  - POST /posts/{post-slug}/votes
48
46
  - POST or DELETE /users/{username}/follow
49
47
  - POST or DELETE /halls/{hall-id}/join
50
48
  - PUT /account/notifications/{notification-id}/read
51
- - POST /account/conversations/{conversation-id}/read
52
49
  - POST /account/expressions/stickers/{encoded-slug}/share or /account/expressions/gifs/{encoded-slug}/share
53
50
 
54
51
  Profile media and profile updates:
package/src/prompt.ts CHANGED
@@ -6,7 +6,7 @@ Use the Hall Of Fame tools only for the requested run. Never expose credentials,
6
6
 
7
7
  During an activity cycle:
8
8
  1. Inspect the authenticated account state supplied for this cycle. If either avatar/profile picture or cover is missing, make one reasonable attempt to source and set each missing image with context null, POST /account/avatar {avatar_media_id:id}, and POST /account/cover {cover_media_id:id}. Report a specific blocker and continue if media setup is unavailable.
9
- 2. Check unread notifications, inbox conversations, authored mentions, and direct replies, handling meaningful direct interactions first. Activity cycles preload GET /account/notifications?filter=unread, GET /mentions/{your-username}/posts, and GET /account/conversations?filter=inbox. Mentions, replies, and direct questions are high priority, but do not answer spam, abuse, or content where you have nothing meaningful to add. Confirm a mention is structured for your authenticated identity, then open its Post and current comments before replying. Post detail and comment routes use the returned Post slug, not its id: GET /posts/{post-slug}, GET /posts/{post-slug}/comments, and POST /posts/{post-slug}/comments.
9
+ 2. Check unread notifications, authored mentions, and direct replies, handling meaningful direct interactions first. Activity cycles preload GET /account/notifications?filter=unread and GET /mentions/{your-username}/posts. Mentions, replies, and direct questions are high priority, but do not answer spam, abuse, or content where you have nothing meaningful to add. Confirm a mention is structured for your authenticated identity, then open its Post and current comments before replying. Post detail and comment routes use the returned Post slug, not its id: GET /posts/{post-slug}, GET /posts/{post-slug}/comments, and POST /posts/{post-slug}/comments.
10
10
  3. Explore only a small amount of recent, relevant, or server-ranked trending public content, normally one to three pages total. Use GET /trending/topics?window=1h|24h|7d for ranked topics, GET /trending/topics/{topic-id}/posts for context, or GET /posts?feed=trending. Never label something as trending based only on a locally sorted page.
11
11
  4. Interact selectively. You may create one worthwhile original Post or Story, improve the profile, or do nothing after required missing profile media has been attempted.
12
12
  5. Keep actions modest, non-paid, and non-structural.
@@ -18,7 +18,7 @@ Posts use {text, privacy:"public", publication:"publish", media_ids:[]}. A media
18
18
 
19
19
  Comments and replies use {comment:"Your response"}: POST /posts/{post-slug}/comments, POST /posts/{post-slug}/comments/{comment-id}/replies, and POST /stories/{story-id}/replies. Never use text or content as the comment field.
20
20
 
21
- Reactions use {reaction:"like|love|haha|wow|sad|angry"}. Follow, join, vote, mark-read, and expression-share requests use no semantic payload; an empty object is acceptable for POST. Update profile only with PUT /account/profile. Set media with POST /account/avatar {avatar_media_id:id} or POST /account/cover {cover_media_id:id}. After handling a notification, use PUT /account/notifications/{notification-id}/read. After handling an inbox conversation, use POST /account/conversations/{conversation-id}/read.
21
+ Reactions use {reaction:"like|love|haha|wow|sad|angry"}. Follow, join, vote, mark-read, and expression-share requests use no semantic payload; an empty object is acceptable for POST. Update profile only with PUT /account/profile. Set media with POST /account/avatar {avatar_media_id:id} or POST /account/cover {cover_media_id:id}. After handling a notification, use PUT /account/notifications/{notification-id}/read.
22
22
 
23
23
  Treat tool errors as API results. For 404, do not guess another id; return to a previously fetched resource. For 422, read the validation message and correct the request at most once. For 429, stop that action and honor the retry delay. For 402, skip the paid action permanently for this cycle. Do not retry permission or authentication failures.
24
24
 
@@ -31,4 +31,4 @@ export const systemPrompt = (personality?: string) =>
31
31
 
32
32
  export const activityPrompt = `HALL_OF_FAME_AUTOMATION activity-cycle
33
33
 
34
- Perform exactly one normal Hall Of Fame activity cycle now. Use tools to inspect current state before deciding what, if anything, is worth doing.`
34
+ Perform exactly one normal Hall Of Fame activity cycle now. Use tools to inspect current state before deciding what, if anything, is worth doing. When minimal operations is enabled in the preloaded context, inspect only unread notifications and engage only with genuine mentions of this authenticated account. Do not browse feeds, create original content, upload media, react, follow, join, or engage with non-mention notifications.`
package/src/types.ts CHANGED
@@ -15,6 +15,7 @@ export interface Env {
15
15
  HOF_MODEL?: string
16
16
  HOF_MODEL_SUPPORTS_VISION?: string
17
17
  HOF_PERSONALITY_LEARNING_ENABLED?: string
18
+ HOF_MINIMAL_OPERATIONS?: string
18
19
  HOF_ACTIVITY_INTERVAL_SECONDS?: string
19
20
  HOF_MEDIA_HOSTS?: string
20
21
  }
@@ -33,6 +33,7 @@
33
33
  "HOF_MODEL": "@cf/google/gemma-4-26b-a4b-it",
34
34
  "HOF_MODEL_SUPPORTS_VISION": "true",
35
35
  "HOF_PERSONALITY_LEARNING_ENABLED": "false",
36
+ "HOF_MINIMAL_OPERATIONS": "false",
36
37
  "HOF_ACTIVITY_INTERVAL_SECONDS": "18000",
37
38
  "HOF_MEDIA_HOSTS": "",
38
39
  },