feat(api): LoopRegistry + REST /api/loops (daemon-native loop lifecycle)#1260
Open
kienbui-eup wants to merge 3 commits into
Open
feat(api): LoopRegistry + REST /api/loops (daemon-native loop lifecycle)#1260kienbui-eup wants to merge 3 commits into
kienbui-eup wants to merge 3 commits into
Conversation
Daemon-native, project-aware loop registry so loops have a first-class
lifecycle and can be inspected by any client, not just the editor.
- loops.rs: LoopRecord (id, pattern_id, workspace, level, trigger, safety,
manifest_toml, turn_prompt, agent_id, binding_id, enabled) + LoopRegistry
persisted atomically to ~/.openfang/loops.json (tmp + rename), loaded at boot.
The daemon stores and orchestrates loops but treats the client-rendered
manifest and turn prompt as opaque - the pattern library stays editor-side.
- AppState carries the registry; endpoints reuse the existing kernel:
- POST /api/loops spawn_agent + cron_create (every/cron) or
register_trigger (event), then persist the record
- GET /api/loops list records
- DELETE /api/loops/{id} remove binding + kill_agent (state files kept)
- POST /api/loops/{id}/run send the turn prompt to the agent
- PUT /api/loops/{id}/level kill + respawn + rebind at a new level
- GET /api/loops/{id}/runs read the workspace loop-run-log.md
- Cron job names are sanitised (CronJob::validate rejects ':'), loop ids are
server-generated uuids (no path traversal in the run-log read).
Verified: cargo check -p openfang-api and --tests EXIT 0; the job JSON matches
CronSchedule/CronAction/CronDelivery field-by-field against the kernel.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Round out the /api/loops lifecycle so a client can pause/resume and edit a loop
without deleting and recreating it.
- POST /api/loops/{id}/enable {enabled}: flips the schedule binding
(cron_scheduler.set_enabled for every/cron, set_trigger_enabled for event) and
persists record.enabled.
- PUT /api/loops/{id}: replaces the loop's config - remove old binding, kill old
agent, spawn from the new manifest, rebind from the new trigger/param, and
update trigger/safety/level/prompt while keeping id/pattern_id/workspace/
created_at. Cron names go through sanitize_cron_name. On a mid-update failure
the record is left pointing at no dead agent (agent_id/binding_id cleared).
Reuses set_loop_level, toggle_cron_job and the create_loop binding block.
Verified: cargo check -p openfang-api and --tests EXIT 0; route table has a single
/api/loops/{id} entry (.delete().put()), no double registration.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Daemon-native, project-aware loop registry so scheduled/event-driven agent loops have a first-class lifecycle and can be inspected by any client (not just the editor).
What
openfang-api/src/loops.rs:LoopRecord+LoopRegistrypersisted atomically to~/.openfang/loops.json(tmp + rename), loaded at boot. The daemon stores + orchestrates loops but treats the client-renderedmanifest_toml+turn_promptas opaque — the pattern/prompt library stays client-side./api/loops(reuses existing kernel primitives):POST /api/loops—spawn_agent+cron_create(every/cron) orregister_trigger(event), persist recordGET /api/loops— list recordsDELETE /api/loops/{id}— remove binding +kill_agent(workspace state files kept)POST /api/loops/{id}/run— send the turn prompt to the agentPUT /api/loops/{id}/level— kill + respawn + rebind at a new rollout levelGET /api/loops/{id}/runs— read the workspaceloop-run-log.mdCronJob::validaterejects:); loop ids are server-generated uuids (no path traversal in the run-log read).Verification
cargo check -p openfang-apiand--testsEXIT 0. The loop→cron job JSON was checked field-by-field againstCronSchedule/CronAction/CronDelivery. Editor thin-client that consumes this API: kienbui-eup/AntiDev#2.🤖 Generated with Claude Code