fix: real per-agent slot handle for multi-agent calendar tools #7

Merged
hzhang merged 1 commits from fix/multi-agent-scheduler-handle into main 2026-05-21 09:39:51 +00:00
Contributor

In multi-agent sync mode every harborforge_calendar_* tool was returning calendarScheduler.<method> is not a function. After PR #6 wired the multi-agent path, index.ts replaced the typed calendarScheduler: CalendarScheduler | null with a { stop() } stub right after starting the runSync/runCheck intervals. Every other method on the slot — isRunning, getCurrentSlot, complete/abort/pause/resumeCurrentSlot, getState, isRestartPending, getStateFilePath — blew up at call time.

What this PR does

Replaces the stub with a MultiAgentSchedulerHandle that:

  • tracks the last slot dispatched per agent (recorded by wakeAgent)
  • exposes getStatus / complete / abort / pause / resume taking the calling agentId
  • resolves the implicit "current slot" via the wake cursor first, then a cache scan over not_started/deferred/ongoing slots so a tool called between sync windows still finds something sensible
  • PATCHes via a new CalendarBridgeClient.updateSlotAs(agentId, …) so audit headers reflect the real caller (the bridge constructor agentId is 'unused' in multi-agent mode)
  • mirrors isRunning/isProcessing/getState/... so the single-agent fallback (CalendarScheduler) keeps working unchanged

Each calendar tool factory now takes OpenClawPluginToolContext, reads ctx.agentId, and dispatches through the handle. Single-agent branches are preserved behind instanceof checks.

Drops the dead trackSessionCompletion poll loop (only definition, no caller) which referenced the removed completeCurrentSlot. Bumps package.json 0.2.0 → 0.3.2.

Verified in sim

  • Old behaviour reproduced via dind-t2 with HF SIM-T2 wakeup: every harborforge_* call returned not a function
  • Build: npm run build clean
  • Next: redeploy plugin to dind-t2, fresh wake of recruiter against task SIM-T2 should see harborforge_calendar_status return a real slot, and harborforge_calendar_complete actually transition the slot to finished

🤖 Generated with Claude Code

In multi-agent sync mode every `harborforge_calendar_*` tool was returning `calendarScheduler.<method> is not a function`. After PR #6 wired the multi-agent path, `index.ts` replaced the typed `calendarScheduler: CalendarScheduler | null` with a `{ stop() }` stub right after starting the runSync/runCheck intervals. Every other method on the slot — `isRunning`, `getCurrentSlot`, `complete/abort/pause/resumeCurrentSlot`, `getState`, `isRestartPending`, `getStateFilePath` — blew up at call time. ## What this PR does Replaces the stub with a `MultiAgentSchedulerHandle` that: - tracks the last slot dispatched per agent (recorded by `wakeAgent`) - exposes `getStatus / complete / abort / pause / resume` taking the calling `agentId` - resolves the implicit "current slot" via the wake cursor first, then a cache scan over `not_started/deferred/ongoing` slots so a tool called between sync windows still finds something sensible - PATCHes via a new `CalendarBridgeClient.updateSlotAs(agentId, …)` so audit headers reflect the real caller (the bridge constructor `agentId` is `'unused'` in multi-agent mode) - mirrors `isRunning/isProcessing/getState/...` so the single-agent fallback (`CalendarScheduler`) keeps working unchanged Each calendar tool factory now takes `OpenClawPluginToolContext`, reads `ctx.agentId`, and dispatches through the handle. Single-agent branches are preserved behind `instanceof` checks. Drops the dead `trackSessionCompletion` poll loop (only definition, no caller) which referenced the removed `completeCurrentSlot`. Bumps `package.json` 0.2.0 → 0.3.2. ## Verified in sim - Old behaviour reproduced via dind-t2 with HF SIM-T2 wakeup: every `harborforge_*` call returned `not a function` - Build: `npm run build` clean - Next: redeploy plugin to dind-t2, fresh wake of `recruiter` against task SIM-T2 should see `harborforge_calendar_status` return a real slot, and `harborforge_calendar_complete` actually transition the slot to `finished` 🤖 Generated with [Claude Code](https://claude.com/claude-code)
hzhang added 1 commit 2026-05-21 09:39:47 +00:00
In multi-agent sync mode every harborforge_calendar_* tool was returning
`calendarScheduler.<method> is not a function`. The cause: index.ts replaced
`calendarScheduler` (typed `CalendarScheduler | null`) with a `{ stop() }`
stub right after wiring the runSync/runCheck intervals, so `isRunning()`,
`getCurrentSlot()`, `completeCurrentSlot()`, `abortCurrentSlot()`,
`pauseCurrentSlot()`, `resumeCurrentSlot()`, `getState()`,
`isRestartPending()` and `getStateFilePath()` all blew up at call time.

Replaces the stub with a `MultiAgentSchedulerHandle` that:
  - tracks the last slot dispatched per agent (recorded by `wakeAgent`)
  - exposes status/complete/abort/pause/resume taking the calling agentId
  - resolves the implicit "current slot" via woken-cursor first then a
    cache scan over not_started/deferred/ongoing slots
  - PATCHes via `bridge.updateSlotAs(agentId, …)` so audit headers reflect
    the real caller (bridge constructor agentId is 'unused' in multi-agent)
  - mirrors the legacy `isRunning/isProcessing/getState/...` surface so
    the single-agent fallback (`CalendarScheduler`) keeps working unchanged

Each calendar tool factory now takes `OpenClawPluginToolContext`, reads
`ctx.agentId`, and dispatches through the handle. Single-agent path
(when `calendarScheduler` is a real `CalendarScheduler`) is preserved
behind `instanceof` checks.

Drops the dead `trackSessionCompletion` poll loop (only definition, no
caller) which referenced the removed `completeCurrentSlot`. Bumps
plugin version 0.2.0 → 0.3.2.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
hzhang merged commit 98e663a19b into main 2026-05-21 09:39:51 +00:00
Sign in to join this conversation.
No Reviewers
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: zhi/HarborForge.OpenclawPlugin#7