mirror of
https://github.com/rowboatlabs/rowboat.git
synced 2026-05-11 16:22:40 +02:00
feat: live notes — single objective per note replaces multi-track model
Folds the multi-`track:`-array model into one `live:` block per note: a single persistent objective the live-note agent maintains, plus an optional triggers object (`cronExpr` / `windows` / `eventMatchCriteria`, each independently optional). A note is now passive or live — no per-track scopes, no section ownership contract, no `once` trigger. The agent owns the whole body and makes patch-style incremental edits per run. Highlights: - Schema: `track:` array → single `live:` object (`packages/shared/src/live-note.ts`). - Runtime: scheduler / event processor / runner under `core/knowledge/live-note/`, with split `lastAttemptAt` (every run, drives 5-min backoff) vs `lastRunAt` (success only, anchors cycles). `throwOnError` on agent runs surfaces LLM / billing failures into `lastRunError`. - Today.md: regenerated by template v2 (single objective covering overview / calendar / emails / what-you-missed / priorities; existing files renamed to `Today.md.bkp.<stamp>`). - Renderer: `LiveNoteSidebar` mounts inside the editor row (no chat overlap, auto-closes on note switch); toolbar Radio button becomes a status pill; `LiveNotesView` replaces background-agents view. - Copilot: new `live-note` skill with act-first stance, default folder/cadence pickers, and a non-negotiable rule to extend an existing objective rather than add a second one. Shared `KNOWLEDGE_NOTE_STYLE_GUIDE` enforces terse-and-scannable writing across `doc-collab` and the live-note agent. - Analytics: `track_block` use-case → `live_note_agent`; trigger (`manual` / `cron` / `window` / `event`) becomes the Pass-2 sub-use-case, alongside `routing` for Pass 1. Legacy run files with the old value are read-mapped via `LegacyStartEvent` so they stay openable in the runs list. Hard cutover — no back-compat shims for legacy `track:` frontmatter arrays. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
0bf7a55611
commit
dabca3da19
59 changed files with 3816 additions and 3212 deletions
|
|
@ -6,7 +6,7 @@ import { LlmModelConfig } from './models.js';
|
|||
import { AgentScheduleConfig, AgentScheduleEntry } from './agent-schedule.js';
|
||||
import { AgentScheduleState } from './agent-schedule-state.js';
|
||||
import { ServiceEvent } from './service-events.js';
|
||||
import { TrackEvent } from './track.js';
|
||||
import { LiveNoteAgentEvent, LiveNoteSchema } from './live-note.js';
|
||||
import { UserMessageContent } from './message.js';
|
||||
import { RowboatApiConfig } from './rowboat-account.js';
|
||||
import { ZListToolkitsResponse } from './composio.js';
|
||||
|
|
@ -214,8 +214,8 @@ const ipcSchemas = {
|
|||
req: ServiceEvent,
|
||||
res: z.null(),
|
||||
},
|
||||
'tracks:events': {
|
||||
req: TrackEvent,
|
||||
'live-note-agent:events': {
|
||||
req: LiveNoteAgentEvent,
|
||||
res: z.null(),
|
||||
},
|
||||
'models:list': {
|
||||
|
|
@ -611,93 +611,83 @@ const ipcSchemas = {
|
|||
response: z.string().nullable(),
|
||||
}),
|
||||
},
|
||||
// Track channels
|
||||
'track:run': {
|
||||
// Live-note channels
|
||||
'live-note:run': {
|
||||
req: z.object({
|
||||
filePath: z.string(),
|
||||
context: z.string().optional(),
|
||||
}),
|
||||
res: z.object({
|
||||
success: z.boolean(),
|
||||
runId: z.string().nullable().optional(),
|
||||
action: z.enum(['replace', 'no_update']).optional(),
|
||||
summary: z.string().nullable().optional(),
|
||||
contentAfter: z.string().nullable().optional(),
|
||||
error: z.string().optional(),
|
||||
}),
|
||||
},
|
||||
'live-note:get': {
|
||||
req: z.object({
|
||||
id: z.string(),
|
||||
filePath: z.string(),
|
||||
}),
|
||||
res: z.object({
|
||||
success: z.boolean(),
|
||||
summary: z.string().optional(),
|
||||
// Fresh, authoritative live-note object from frontmatter, or null when
|
||||
// the note is passive. Renderer should use this for display/edit —
|
||||
// never a stale cached copy.
|
||||
live: LiveNoteSchema.nullable().optional(),
|
||||
error: z.string().optional(),
|
||||
}),
|
||||
},
|
||||
'track:get': {
|
||||
'live-note:set': {
|
||||
req: z.object({
|
||||
id: z.string(),
|
||||
filePath: z.string(),
|
||||
live: LiveNoteSchema,
|
||||
}),
|
||||
res: z.object({
|
||||
success: z.boolean(),
|
||||
// Fresh, authoritative YAML of the track from frontmatter.
|
||||
// Renderer should use this for display/edit — never a stale cached copy.
|
||||
yaml: z.string().optional(),
|
||||
live: LiveNoteSchema.nullable().optional(),
|
||||
error: z.string().optional(),
|
||||
}),
|
||||
},
|
||||
'track:update': {
|
||||
'live-note:setActive': {
|
||||
req: z.object({
|
||||
id: z.string(),
|
||||
filePath: z.string(),
|
||||
// Partial Track updates — merged into the entry on disk.
|
||||
// Backend is the sole writer; avoids races with scheduler/runner writes.
|
||||
updates: z.record(z.string(), z.unknown()),
|
||||
}),
|
||||
res: z.object({
|
||||
success: z.boolean(),
|
||||
yaml: z.string().optional(),
|
||||
error: z.string().optional(),
|
||||
}),
|
||||
},
|
||||
'track:replaceYaml': {
|
||||
req: z.object({
|
||||
id: z.string(),
|
||||
filePath: z.string(),
|
||||
yaml: z.string(),
|
||||
}),
|
||||
res: z.object({
|
||||
success: z.boolean(),
|
||||
yaml: z.string().optional(),
|
||||
error: z.string().optional(),
|
||||
}),
|
||||
},
|
||||
'track:delete': {
|
||||
req: z.object({
|
||||
id: z.string(),
|
||||
filePath: z.string(),
|
||||
}),
|
||||
res: z.object({
|
||||
success: z.boolean(),
|
||||
error: z.string().optional(),
|
||||
}),
|
||||
},
|
||||
'track:setNoteActive': {
|
||||
req: z.object({
|
||||
path: RelPath,
|
||||
active: z.boolean(),
|
||||
}),
|
||||
res: z.object({
|
||||
success: z.boolean(),
|
||||
note: z.object({
|
||||
path: RelPath,
|
||||
trackCount: z.number().int().positive(),
|
||||
createdAt: z.string().nullable(),
|
||||
lastRunAt: z.string().nullable(),
|
||||
isActive: z.boolean(),
|
||||
}).optional(),
|
||||
live: LiveNoteSchema.nullable().optional(),
|
||||
error: z.string().optional(),
|
||||
}),
|
||||
},
|
||||
'track:listNotes': {
|
||||
'live-note:delete': {
|
||||
req: z.object({
|
||||
filePath: z.string(),
|
||||
}),
|
||||
res: z.object({
|
||||
success: z.boolean(),
|
||||
error: z.string().optional(),
|
||||
}),
|
||||
},
|
||||
'live-note:stop': {
|
||||
req: z.object({
|
||||
filePath: z.string(),
|
||||
}),
|
||||
res: z.object({
|
||||
success: z.boolean(),
|
||||
error: z.string().optional(),
|
||||
}),
|
||||
},
|
||||
'live-note:listNotes': {
|
||||
req: z.null(),
|
||||
res: z.object({
|
||||
notes: z.array(z.object({
|
||||
path: RelPath,
|
||||
trackCount: z.number().int().positive(),
|
||||
createdAt: z.string().nullable(),
|
||||
lastRunAt: z.string().nullable(),
|
||||
isActive: z.boolean(),
|
||||
objective: z.string(),
|
||||
})),
|
||||
}),
|
||||
},
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue