Skip to content

feat(gastown): add cloud-native nudge system#1079

Open
jrf0110 wants to merge 1 commit intomainfrom
1032-cloud-native-nudge-system
Open

feat(gastown): add cloud-native nudge system#1079
jrf0110 wants to merge 1 commit intomainfrom
1032-cloud-native-nudge-system

Conversation

@jrf0110
Copy link
Contributor

@jrf0110 jrf0110 commented Mar 12, 2026

Summary

  • Adds agent_nudges table and TownDO nudge queue methods (queueNudge, getPendingNudges, acknowledgeNudge) for reliable message delivery to agents
  • Changes non-mayor agent lifecycle: on session.idle, agents now check for pending nudges before exiting — if nudges are pending, they inject the next nudge as a follow-up prompt and stay alive ("idle-but-available" state)
  • Adds gt_nudge tool for inter-agent messaging with wait-idle, immediate, and queue delivery modes
  • Adds nudge() client method and handleNudge worker endpoint for the container→worker nudge path
  • Replaces sendMail with queueNudge in patrol functions for time-sensitive messages (GUPP warnings, rework requests, merge-ready notifications)
  • Dashboard shows pending nudge counts per agent

Closes #1032

Verification

  • Code review of diff against fork branch — all patches applied cleanly to origin/main
  • Mayor tools test file updated with gt_nudge tool registration

Visual Changes

N/A (pending nudge counts on dashboard added but not visually verified)

Reviewer Notes

  • This is the largest change in the batch (13 files, ~860 lines added) — the core behavioral change is in process-manager.ts where session.idle no longer terminates non-mayor agents unconditionally
  • The idle-but-available pattern means agents will stay alive longer, consuming container resources — monitor container memory in production
  • The agent_nudges table is in TownDO SQLite; ensure the schema migration runs on existing towns
  • Patrol functions (patrol.ts) now use queueNudge instead of sendMail — this changes the delivery semantics from "push and hope" to "queue and deliver on next idle"

…ge delivery

Replaces fire-and-hope mail push with a nudge queue that guarantees delivery.
Adds agent_nudges table and TownDO nudge queue methods (queueNudge,
getPendingNudges, acknowledgeNudge). Non-mayor agents enter idle-but-available
state on session.idle instead of exiting, checking for pending nudges before
terminating. Adds gt_nudge tool for inter-agent messaging, nudge() client
method, and handleNudge worker endpoint. Patrol functions use queueNudge
instead of sendMail for time-sensitive messages. Dashboard shows pending
nudge counts.

Closes #1032

let expiresAt: string | null = null;
if (mode === 'queue' && options?.ttlSeconds != null) {
expiresAt = new Date(Date.now() + options.ttlSeconds * 1000).toISOString();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WARNING: TTL timestamps are stored in a format that SQLite won't compare correctly

expires_at is written with toISOString() (2026-03-12T10:00:00.000Z), but the pending/expiry queries compare it to datetime('now') (2026-03-12 10:00:00). On the same day, the T vs space mismatch makes expired nudges still sort as future rows, so queued nudges can survive long past their TTL. Store the timestamp with the same SQLite format you query against, or compare via strftime(...)/unix epoch instead.

WHERE ${agent_nudges.agent_bead_id} = ?
AND ${agent_nudges.source} = 'witness'
AND ${agent_nudges.message} LIKE '%GUPP_ESCALATION%'
AND ${agent_nudges.delivered_at} IS NULL
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WARNING: Successful immediate nudges will be re-sent on every patrol tick

This dedupe check only looks for rows where delivered_at IS NULL, but both GUPP paths call queueNudge(..., { mode: 'immediate' }), which marks a successful send as delivered immediately. After the first successful nudge, the next 5s alarm tick won't find any matching row here and will enqueue/deliver the same GUPP escalation again. The same problem exists in the GUPP_CHECK query below.

+ '<div class="nudge-meta">source: ' + esc(n.source) + ' | mode: ' + esc(n.mode) + ' | priority: ' + esc(n.priority) + ' | ' + esc(n.created_at ?? '') + '</div>'
+ '<div class="nudge-msg">' + esc(preview) + '</div>'
+ '<div style="margin-top:4px">'
+ '<button class="primary" style="font-size:11px;padding:2px 8px" onclick="deliverNudgeNow(\\'' + agentId + '\\', ' + JSON.stringify(n.message).replace(/</g, '\\\\u003c') + ')">Deliver Now</button>'
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WARNING: This generates invalid HTML for the Deliver Now button

JSON.stringify(n.message) produces a raw double-quoted JS string, so the final markup looks like onclick="deliverNudgeNow('agent', "message")" and the attribute terminates early. As written, the button won't be reliably clickable. Put the message in a data-* attribute or escape it for HTML before interpolating it into the handler.

@kilo-code-bot
Copy link
Contributor

kilo-code-bot bot commented Mar 12, 2026

Code Review Summary

Status: 3 Issues Found | Recommendation: Address before merge

Fix these issues in Kilo Cloud

Overview

Severity Count
CRITICAL 0
WARNING 3
SUGGESTION 0
Issue Details (click to expand)

WARNING

File Line Issue
cloudflare-gastown/src/dos/Town.do.ts 758 expires_at is stored as ISO 8601 but later compared to datetime('now'), so same-day TTL checks never evaluate correctly.
cloudflare-gastown/src/dos/town/patrol.ts 238 GUPP dedupe only checks undelivered nudges, so successful immediate nudges will be sent again on the next patrol tick.
cloudflare-gastown/src/ui/dashboard.ui.ts 667 The Deliver Now button interpolates JSON.stringify(n.message) directly into onclick, producing invalid HTML/JS.
Other Observations (not in diff)

Issues found in unchanged code that cannot receive inline comments:

File Line Issue
cloudflare-gastown/src/dos/Town.do.ts 3577 getAlarmStatus() still counts GUPP warnings/escalations from message beads, so the status dashboard will underreport the new nudge-based patrol signals.
Files Reviewed (13 files)
  • cloudflare-gastown/container/plugin/client.ts - 0 issues
  • cloudflare-gastown/container/plugin/mayor-tools.test.ts - 0 issues
  • cloudflare-gastown/container/plugin/mayor-tools.ts - 0 issues
  • cloudflare-gastown/container/plugin/tools.ts - 0 issues
  • cloudflare-gastown/container/src/control-server.ts - 0 issues
  • cloudflare-gastown/container/src/process-manager.ts - 0 issues
  • cloudflare-gastown/src/db/tables/agent-nudges.table.ts - 0 issues
  • cloudflare-gastown/src/dos/Town.do.ts - 1 issue
  • cloudflare-gastown/src/dos/town/patrol.ts - 1 issue
  • cloudflare-gastown/src/gastown.worker.ts - 0 issues
  • cloudflare-gastown/src/handlers/mayor-tools.handler.ts - 0 issues
  • cloudflare-gastown/src/handlers/rig-agents.handler.ts - 0 issues
  • cloudflare-gastown/src/ui/dashboard.ui.ts - 1 issue

Reviewed by gpt-5.4-20260305 · 1,368,071 tokens

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Cloud-native nudge system — reliable real-time message delivery to agents

2 participants