Add agentctl heartbeat/startup events and handshake metadata.

This extends ping with handshake graph metadata and emits runtime liveness/startup sidecar events so attached diagnostics can verify readiness progression and module graph parity in real time.

Co-authored-by: Cursor <[email protected]>
This commit is contained in:
Raven Scott
2026-06-02 09:35:56 -04:00
co-authored by Cursor
parent 5584473a07
commit 99ec49ec85
2 changed files with 26 additions and 3 deletions
+4 -2
View File
@@ -283,7 +283,7 @@ bare ../../modules/pearcord-agentctl/bin/agentctl.cjs ping
bare ../../modules/pearcord-agentctl/bin/agentctl.cjs wait-ready --timeout 20000
```
`ping` output includes `host`, `port`, and `tokenMode` to make attached-run diagnostics scriptable.
`ping` output includes `host`, `port`, `tokenMode`, and `handshake` metadata (`appVersion`, `platformVersion`, `moduleGraphHash`, `pearcordModuleCount`) for runtime graph verification.
`doctor` includes `listeningEvidence` from `pearcord.log`, surfacing the most recent worker-side `agentctl listening` line and host/port match status.
One-shot live harness:
@@ -333,7 +333,7 @@ One JSON object per line (newline-delimited).
### Responses
```json
{ "id": 1, "ok": true, "view": { ... } }
{ "id": 1, "ok": true, "pong": true, "version": "0.8.638", "handshake": { "moduleGraphHash": "..." } }
{ "id": 2, "ok": false, "error": "wait timeout", "view": { ... } }
```
@@ -342,6 +342,8 @@ One JSON object per line (newline-delimited).
```json
{ "event": "state", "view": { ... } }
{ "event": "sidecar", "payload": { "type": "error", "message": "..." }, "at": 1734567890123 }
{ "event": "sidecar", "payload": { "type": "agentctl-heartbeat", "mode": "guild", "sessionReady": true, "at": 1734567890123 }, "at": 1734567890123 }
{ "event": "sidecar", "payload": { "type": "agentctl-startup", "phase": "agentctl-listening", "bindLatencyMs": 42 }, "at": 1734567890123 }
```
Set `PEARCORD_AGENTCTL_TOKEN` to require the same secret on every request when the server is enabled.
+22 -1
View File
@@ -28,6 +28,7 @@ class AgentServer {
/** @type {Array<object>} */
this._eventLog = []
this._eventLogMax = 200
this._handshakeMeta = opts.handshakeMeta || null
}
get lastView () {
@@ -67,6 +68,25 @@ class AgentServer {
this.broadcast(evt)
}
notifyHeartbeat (payload = {}) {
this.notifySidecar({
type: 'agentctl-heartbeat',
...payload
})
}
notifyStartupCheckpoint (phase, details = {}) {
this.notifySidecar({
type: 'agentctl-startup',
phase,
...details
})
}
setHandshakeMeta (meta) {
this._handshakeMeta = meta || null
}
_pushEvent (evt) {
this._eventLog.push(evt)
while (this._eventLog.length > this._eventLogMax) this._eventLog.shift()
@@ -238,7 +258,8 @@ class AgentServer {
...base,
ok: true,
pong: true,
version: process.env.PEARCORD_APP_VERSION || null
version: process.env.PEARCORD_APP_VERSION || null,
handshake: this._handshakeMeta
})
return
}