Scope lead notes by project and share them with task agents
This commit is contained in:
@@ -1580,6 +1580,7 @@ components:
|
||||
properties:
|
||||
id: { type: string }
|
||||
text: { type: string }
|
||||
project_id: { type: string, description: "The project the note applies to; absent for a note that applies to every project." }
|
||||
created_at: { type: string, format: date-time }
|
||||
updated_at: { type: string, format: date-time }
|
||||
|
||||
|
||||
+3
-1
@@ -206,7 +206,9 @@ Each user has one long-running lead agent, shown in the left pane. It is not tie
|
||||
- **Move hold.** Each `PUT /api/me/focus` from the user (switching task or tab, by any means: the switcher, a tab, a link, a notification, or the page's own switch to Inbox when a question arrives) records the time in `Manager.humanMoved`, and for 10 seconds after it (`moveHold`) every `show` that would change the view is refused, whatever the pane lock above would allow, with an error giving how long ago the move was. A `show` of what the pane already shows still applies its tab order. When the 10 seconds pass without another move by the user (`endMoveHold`, on a timer), byre clears it and sends the lead the signal "The move hold is over: …", every time, whether or not a `show` was refused; its guidance tells it to show what it held back if the pane's other rules allow, or reply `NO_REPLY`. It is in memory, so a restart clears it.
|
||||
- **Start hold.** Start on a TODO item in the Project tab leaves the pane where it is. When the user starts one themselves (`StartTodo` with the user as actor, not the lead's `start_todo`), byre records the time in `Manager.startedTodo` for them, and for 10 seconds after it (`startHold`), while the pane's selected tab is `project`, every `show` that would change the view is refused, focused or not, with an error telling the lead the human is likely to start more. The user's own `PUT /api/me/focus` (switching task or tab) or a message to the lead clears it. The refusal and the `show` tool's description both tell the lead the hold lasts 10 seconds. When the 10 seconds pass without it being cleared or restarted by another Start (`endStartHold`, on a timer), byre clears it and, if the pane's selected tab is still `project`, sends the lead the signal "The start hold is over: …" so it can show what it held back; its guidance tells it to reply `NO_REPLY` when nothing was. It is in memory, so a restart clears it.
|
||||
- **Compaction.** The pane's **Compact** button asks the lead for a handoff (`leadHandoffPrompt`). The lead can't see its own context size, so the first time one of its turns ends (other than a handoff or a `compact_self` turn) with the latest `session.context` `used` at a quarter of its `window` or more, byre holds a signal for it giving the percentage and asking it to call `compact_self` at the next logical point (`warnLeadContext` in `internal/agents/lead.go`); it is sent once per live process, so a byre restart can send it again once the window is known. The lead calls `compact_self`; when that turn ends normally, byre sends the same request. The handoff turn ends the session, and a new lead session starts from `leadHandoffPreamble` plus the handoff; held input follows once that turn ends.
|
||||
- **Notes.** The lead keeps notes on how its user works (priorities, preferences, the reasoning behind choices) in `lead_notes`, one row per note keyed by `owner`, through its `remember` (add, or replace the text of an `id`), `forget` and `list_notes` tools (`internal/agents/leadnotes.go`). Each change records a `lead.note_saved` or `lead.note_deleted` event owned by the user. When a lead's agent process starts, `notesText` lists every note with its id after `leadGuidance` in the session's instructions (`--append-system-prompt` for Claude, which keeps what the session's first request carried; `developerInstructions` for Codex), but not in the per-turn guidance reminder, so a new or compacted lead session starts from the current notes and notes written mid-session are already in its context. The guidance tells the lead when to write a note (an answer that departs from a recommendation, a correction, a stated preference), to cite a note's id when it shapes a recommendation, and that it may answer a task agent's question itself when a note settles it outright. `GET /api/me/lead/notes` lists the caller's notes and `DELETE /api/me/lead/notes/{noteId}` deletes one (`Manager.DeleteLeadNote`), and, when the user has a live lead session, holds a message for it from `byre` naming the note's id and text and telling it to stop applying it, since the running session still has the note in its instructions. It is held as a message, not a signal, so it keeps its place in line with the human's own messages: it goes out ahead of any message they send after the delete, as its own turn (drawn like a signal batch's, and folded away on `NO_REPLY`) or mid-turn after the lead's next tool call; the Settings tab lists them with a delete link each, refetching on either event. `sqlite3 byre.db "SELECT owner, id, text FROM lead_notes ORDER BY created_at"` shows them.
|
||||
- **Notes.** The lead keeps notes on how its user works (priorities, preferences, the reasoning behind choices) in `lead_notes`, one row per note keyed by `owner`, through its `remember` (add, or replace the text and project of an `id`), `forget` and `list_notes` tools (`internal/agents/leadnotes.go`). A note has a `project_id` when it applies to one project and none (the column empty) when it applies to the user in every project; `remember` takes `project_id` and refuses one that names no project, and the guidance tells the lead to file a note about one project's code, UI, docs or process under it and to apply it only to that project's tasks. Each change records a `lead.note_saved` or `lead.note_deleted` event owned by the user. When a lead's agent process starts, `notesText` lists every note with its id after `leadGuidance`, the every-project notes first and then each project's under its name and id, in the session's instructions (`--append-system-prompt` for Claude, which keeps what the session's first request carried; `developerInstructions` for Codex), but not in the per-turn guidance reminder, so a new or compacted lead session starts from the current notes and notes written mid-session are already in its context. The guidance tells the lead when to write a note (an answer that departs from a recommendation, a correction, a stated preference), to cite a note's id when it shapes a recommendation, and that it may answer a task agent's question itself when a note settles it outright. `GET /api/me/lead/notes` lists the caller's notes and `DELETE /api/me/lead/notes/{noteId}` deletes one (`Manager.DeleteLeadNote`), and, when the user has a live lead session, holds a message for it from `byre` naming the note's id and text and telling it to stop applying it, since the running session still has the note in its instructions. It is held as a message, not a signal, so it keeps its place in line with the human's own messages: it goes out ahead of any message they send after the delete, as its own turn (drawn like a signal batch's, and folded away on `NO_REPLY`) or mid-turn after the lead's next tool call; the Settings tab shows one set at a time, picked from a dropdown of Every project and each project by name with its note count (the pick is kept in memory across tab switches), each note with a small delete button styled like the transcript's, refetching on either event. `sqlite3 byre.db "SELECT owner, project_id, id, text FROM lead_notes ORDER BY created_at"` shows them.
|
||||
|
||||
Task agents get notes too. An implementer session's instructions end with `taskNotesGuidance` and `notesText` for the task's creator, limited to the every-project notes and the task's project's (`startLive` in `internal/agents/manager.go`), fixed when the agent process starts like the lead's. Reviewers get none. Implementers have a `suggest_note` tool (`text`, `evidence`) that queues a lead signal for the task's creator naming the task, the suggested text, the evidence and the project id, and refuses when the creator has no live lead session. The lead's guidance tells it to save a suggestion with `remember`, choosing its project, only when it holds and isn't already covered, and to tell the human nothing either way. A suggestion leaves no record of its own beyond the signal; a saved one shows as the lead's `lead.note_saved`.
|
||||
- **Restarts.** On shutdown, byre marks each running lead session idle with "byre stopped mid-turn" and holds a restart notice for it, so a lead is never left `running` for a build without lead support to find. After a crash, startup resumes a running lead like a task session. Startup then delivers any held input.
|
||||
|
||||
The UI is two panes filling the window, with no navbar. The left has three tabs: Lead, the lead's transcript and composer (the same view as a task transcript, with only the Compact button in its header), Status and Settings, and at the end of its tab strip the connection dot and the user's name. Which left tab is selected is kept only in the page; the Lead view stays mounted while another tab shows, and Status or Settings mounts each time its tab is selected. The right shows the focused task, or project with no task: a top row holding the task picker, centred, and the task's preview links at its right end (see "Previews"), then the tab strip, then the selected tab's view with its own header. The tabs are Transcript, Changes, Files, Terminal, Inbox, Reviews, Workflow and Project, the lead's `tabs` first. Reviews shows only once it has a `review.*` event, Inbox only while the task has open items, and Workflow only on a task with a `workflow_run` (see "Workflows"); the selected tab always shows. Tabs that don't fit in the strip move, from the end, into a **More ▾** menu (`web/src/panes.ts`); the selected tab never moves there, and More carries the Inbox highlight and count when Inbox is in it. ⌘P (Ctrl+P elsewhere) selects the Project tab, keeping the right pane's task, and replaces the browser's print shortcut. ⌘L (Ctrl+L elsewhere) selects the Lead tab and puts the cursor in the lead's composer, and replaces the browser's address bar shortcut. ⌘S (Ctrl+S elsewhere) opens the right pane's task's Merge confirmation, from any tab, when the Merge button would be enabled, and replaces the browser's save shortcut. ⌘/ (Ctrl+/ elsewhere) opens a dialog listing every keyboard shortcut; the list is `shortcuts` in `web/src/switcher.ts`, kept in step with the handlers by hand. The ⌘ shortcuts do nothing while a dialog is open.
|
||||
|
||||
@@ -26,7 +26,7 @@ const leadGuidance = `You are the human's lead agent in byre, shown in the left
|
||||
- Show, don't tell. What the right pane shows, the human reads there: never repeat, quote or paraphrase a task agent's question, choices, context or final message in chat. Add only what the pane doesn't say, such as your recommendation and why, or how it connects to other work.
|
||||
- Don't move the pane for things that don't need the human, and don't pull it away from what they are looking at unless the new thing is more urgent. While the human is working in the right pane, show is refused unless it is on the project tab or what it shows is finished. It is also refused on the project tab for 10 seconds after the human starts a TODO item there, since they are likely to start more. It is always refused for 10 seconds after the human moves the pane themselves, since they just picked what to look at. When show is refused, say in chat what you would have shown, and carry on. "The start hold is over" means those 10 seconds passed with the pane still on the project tab, and "The move hold is over" means 10 seconds passed since the human's last move: show what you held back, if it still matters and the pane's other rules allow it, or make your final message NO_REPLY.
|
||||
- Questions from task agents: answer one yourself with answer_question only when the answer is clear, for example the agent's recommendation is plainly right given what the human has said, the human already decided it, or one of your notes settles it outright. Otherwise show it to the human with your recommendation and wait. When the human tells you an answer, give it with answer_question. Say which questions you answered and how, naming the note by its id when one decided it.
|
||||
- Notes: you learn how the human works and keep it with remember, forget and list_notes. byre adds every note to your instructions at the start of each lead session, so they outlast compaction; the human can read and delete them in the Settings tab. Write a note when the human shows a priority, a preference or the reasoning behind a choice that will recur: an answer that departs from the agent's recommendation (the answered signal shows both), a correction or overrule of you or a task agent, or a statement of how they want things done. Record the choice and the why, in their terms, general enough to apply to the next case and specific enough to act on, for example "Prefers replacing a state label over adding a tag beside it: keeps the picker uncluttered." Don't note one-off facts about a single task, and don't duplicate: update an existing note with remember and its id instead. When the human contradicts a note, update it or forget it. Before recommending anything, check your notes, and when one shaped the recommendation, cite its id.
|
||||
- Notes: you learn how the human works and keep it with remember, forget and list_notes. byre adds every note to your instructions at the start of each lead session, so they outlast compaction; the human can read and delete them in the Settings tab. Write a note when the human shows a priority, a preference or the reasoning behind a choice that will recur: an answer that departs from the agent's recommendation (the answered signal shows both), a correction or overrule of you or a task agent, or a statement of how they want things done. Record the choice and the why, in their terms, general enough to apply to the next case and specific enough to act on, for example "Prefers replacing a state label over adding a tag beside it: keeps the picker uncluttered." File each note under the project it is about with project_id when it concerns that project's code, UI, docs or process, and with no project_id only when it holds for how the human works everywhere; apply a project's notes only to that project's tasks. Task agents start with their creator's every-project notes and their project's, and suggest new ones with suggest_note, which reaches you as a signal: save a suggestion only when it holds and your notes don't already cover it, choosing its project yourself, and otherwise drop it; neither needs a word to the human. Don't note one-off facts about a single task, and don't duplicate: update an existing note with remember and its id instead. When the human contradicts a note, update it or forget it. Before recommending anything, check your notes, and when one shaped the recommendation, cite its id.
|
||||
- Finished turns: read them and show the human only what matters: work finished and ready to merge, problems, decisions made. Stay quiet about routine progress.
|
||||
- Reviews: after each new snapshot of a task, a reviewer agent from the other vendor reviews the change. The results are on the task's reviews tab, and read_transcript shows each review's findings. They are for the human to look at when they want; mention one only when its findings matter to what the human is deciding.
|
||||
- Dig into context with list_tasks, read_transcript and read_inbox, and with your own Read, Grep and Bash on a task's worktree path (from list_tasks). Don't edit files in task worktrees; ask the task's agent with send_message.
|
||||
|
||||
@@ -16,14 +16,17 @@ func (m *Manager) noteTools(tool func(name, description, schema string, run func
|
||||
return []mcp.Tool{
|
||||
tool("remember",
|
||||
"Save a note about how the human works: a priority, a preference, or reasoning behind a choice, worded so you can apply it later. "+
|
||||
"Without id it adds a note; with id it replaces that note's text. byre gives you every note at the start of each new lead session.",
|
||||
"Give project_id when the note is about one project (its code, UI, docs or process); omit it when it holds for the human in every project. "+
|
||||
"Without id it adds a note; with id it replaces that note's text and project. byre gives you every note at the start of each new lead session.",
|
||||
`{"type": "object", "properties": {
|
||||
"text": {"type": "string"},
|
||||
"project_id": {"type": "string", "description": "The project the note applies to; omit for every project."},
|
||||
"id": {"type": "string", "description": "The note to replace."}
|
||||
}, "required": ["text"]}`,
|
||||
func(ctx context.Context, user api.User, raw json.RawMessage) (string, error) {
|
||||
args := struct {
|
||||
Text string `json:"text"`
|
||||
ProjectID string `json:"project_id"`
|
||||
ID string `json:"id"`
|
||||
}{}
|
||||
err := json.Unmarshal(raw, &args)
|
||||
@@ -34,7 +37,13 @@ func (m *Manager) noteTools(tool func(name, description, schema string, run func
|
||||
if text == "" {
|
||||
return "", errors.New("text is empty")
|
||||
}
|
||||
n, err := m.store.SaveLeadNote(ctx, user.Login, args.ID, text, leadActor(user.Login))
|
||||
if args.ProjectID != "" {
|
||||
_, err = m.store.GetProject(ctx, args.ProjectID)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
n, err := m.store.SaveLeadNote(ctx, user.Login, args.ID, args.ProjectID, text, leadActor(user.Login))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
@@ -66,11 +75,63 @@ func (m *Manager) noteTools(tool func(name, description, schema string, run func
|
||||
"List your notes about how the human works, with their ids, as they are now. The human can delete notes in byre's Settings tab.",
|
||||
`{"type": "object", "properties": {}}`,
|
||||
func(ctx context.Context, user api.User, raw json.RawMessage) (string, error) {
|
||||
return m.notesText(ctx, user.Login)
|
||||
return m.notesText(ctx, user.Login, "")
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
const taskNotesGuidance = "The human's lead agent keeps the notes below on how the human works, in every project and in this one. Follow them in this task. " +
|
||||
"When the human shows you a preference, a priority or the reasoning behind a choice that will recur, such as a correction or an overrule, suggest a note with suggest_note; the lead decides whether to keep it."
|
||||
|
||||
func (m *Manager) suggestNoteTool(l *live) mcp.Tool {
|
||||
return mcp.Tool{
|
||||
Name: "suggest_note",
|
||||
Description: "Suggest a note to the human's lead agent about how the human works: the choice and the why, in their terms, general enough to apply to the next case. " +
|
||||
"The lead decides whether to save it, and for this project or every project. Don't suggest one-off facts about this task or what the notes already say.",
|
||||
InputSchema: json.RawMessage(`{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"text": {"type": "string", "description": "The note, as you would want it worded."},
|
||||
"evidence": {"type": "string", "description": "What the human said or did that shows it."}
|
||||
},
|
||||
"required": ["text", "evidence"]
|
||||
}`),
|
||||
Handler: func(ctx context.Context, raw json.RawMessage) (string, error) {
|
||||
args := struct {
|
||||
Text string `json:"text"`
|
||||
Evidence string `json:"evidence"`
|
||||
}{}
|
||||
err := json.Unmarshal(raw, &args)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("malformed suggest_note call: %w", err)
|
||||
}
|
||||
text := strings.TrimSpace(args.Text)
|
||||
if text == "" {
|
||||
return "", fmt.Errorf("%w: text must not be empty", ErrInvalid)
|
||||
}
|
||||
task, err := m.store.GetTask(ctx, l.taskID)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
_, found, err := m.activeLead(ctx, task.CreatedBy)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if !found {
|
||||
return "", fmt.Errorf("%w: %s has no lead agent to take the suggestion", ErrConflict, task.CreatedBy)
|
||||
}
|
||||
signal := fmt.Sprintf("%s suggests a note on how %s works:\n%s\nEvidence: %s\nSave it with remember if it holds and your notes don't already cover it, under project_id %s or for every project as fits; otherwise drop it.",
|
||||
m.taskLabel(ctx, task.Id), task.CreatedBy, text, strings.TrimSpace(args.Evidence), task.ProjectId)
|
||||
err = m.store.AddLeadInput(ctx, store.LeadInput{Owner: task.CreatedBy, Kind: store.LeadSignal, Actor: "byre", Text: signal})
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
m.deliverLead(task.CreatedBy)
|
||||
return "Suggestion sent to the lead.", nil
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (m *Manager) DeleteLeadNote(ctx context.Context, login, id string) error {
|
||||
text, err := m.store.DeleteLeadNote(ctx, login, id, login)
|
||||
if err != nil {
|
||||
@@ -89,18 +150,41 @@ func (m *Manager) DeleteLeadNote(ctx context.Context, login, id string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Manager) notesText(ctx context.Context, owner string) (string, error) {
|
||||
func (m *Manager) notesText(ctx context.Context, owner, onlyProject string) (string, error) {
|
||||
notes, err := m.store.LeadNotes(ctx, owner)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if len(notes) == 0 {
|
||||
return fmt.Sprintf("You have no notes yet on how %s works.", owner), nil
|
||||
projects, err := m.store.ListProjects(ctx)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
b := &strings.Builder{}
|
||||
fmt.Fprintf(b, "Your notes on how %s works (%d):", owner, len(notes))
|
||||
writeNotes(b, notes, "", fmt.Sprintf("Notes on how %s works in every project", owner))
|
||||
for _, p := range projects {
|
||||
if onlyProject != "" && p.Id != onlyProject {
|
||||
continue
|
||||
}
|
||||
writeNotes(b, notes, p.Id, fmt.Sprintf("Notes on how %s works in project %s (%s)", owner, p.Name, p.Id))
|
||||
}
|
||||
if b.Len() == 0 {
|
||||
return fmt.Sprintf("There are no notes yet on how %s works.", owner), nil
|
||||
}
|
||||
return strings.TrimPrefix(b.String(), "\n\n"), nil
|
||||
}
|
||||
|
||||
func writeNotes(b *strings.Builder, notes []api.LeadNote, projectID, heading string) {
|
||||
scoped := []api.LeadNote{}
|
||||
for _, n := range notes {
|
||||
if deref(n.ProjectId) == projectID {
|
||||
scoped = append(scoped, n)
|
||||
}
|
||||
}
|
||||
if len(scoped) == 0 {
|
||||
return
|
||||
}
|
||||
fmt.Fprintf(b, "\n\n%s (%d):", heading, len(scoped))
|
||||
for _, n := range scoped {
|
||||
fmt.Fprintf(b, "\n- [%s] %s", n.Id, n.Text)
|
||||
}
|
||||
return b.String(), nil
|
||||
}
|
||||
|
||||
@@ -685,6 +685,12 @@ func (m *Manager) startLive(ctx context.Context, sess api.AgentSession, task api
|
||||
tools := append([]mcp.Tool{m.askTool(l), m.cancelQuestionTool(l), m.setMergeTitleTool(l), m.stopMergeTool(l), m.setTaskSummaryTool(l), m.searchClosedTool(l), m.exposePortTool(l)}, m.subtaskTools(l)...)
|
||||
if sess.Role == api.Implementer {
|
||||
g += longTitleGuidance(task)
|
||||
notes, err := m.notesText(ctx, task.CreatedBy, task.ProjectId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
l.notes = "\n\n" + taskNotesGuidance + "\n\n" + notes
|
||||
tools = append(tools, m.suggestNoteTool(l))
|
||||
}
|
||||
if sess.Role == api.Implementer && task.WorkflowStage != nil {
|
||||
frozen, err := m.store.TaskWorkflow(ctx, task.Id)
|
||||
@@ -716,7 +722,7 @@ func (m *Manager) startLive(ctx context.Context, sess api.AgentSession, task api
|
||||
return nil, err
|
||||
}
|
||||
g = leadGuidance
|
||||
notes, err := m.notesText(ctx, l.owner)
|
||||
notes, err := m.notesText(ctx, l.owner, "")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
+175
-172
@@ -804,6 +804,9 @@ type InboxState string
|
||||
type LeadNote struct {
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
Id string `json:"id"`
|
||||
|
||||
// ProjectId The project the note applies to; absent for a note that applies to every project.
|
||||
ProjectId *string `json:"project_id,omitempty"`
|
||||
Text string `json:"text"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
}
|
||||
@@ -3487,178 +3490,178 @@ var swaggerSpec = []string{
|
||||
"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",
|
||||
"ilCerhrqDs/Y2+Wxd5/fq4vDx4//LwAA//8=",
|
||||
"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",
|
||||
}
|
||||
|
||||
// decodeSpec returns the embedded OpenAPI spec as raw JSON bytes,
|
||||
|
||||
@@ -11,7 +11,7 @@ import (
|
||||
)
|
||||
|
||||
func (s *Store) LeadNotes(ctx context.Context, owner string) ([]api.LeadNote, error) {
|
||||
rows, err := s.db.QueryContext(ctx, `SELECT id, text, created_at, updated_at FROM lead_notes WHERE owner = ? ORDER BY created_at, id`, owner)
|
||||
rows, err := s.db.QueryContext(ctx, `SELECT id, text, project_id, created_at, updated_at FROM lead_notes WHERE owner = ? ORDER BY created_at, id`, owner)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -20,12 +20,16 @@ func (s *Store) LeadNotes(ctx context.Context, owner string) ([]api.LeadNote, er
|
||||
notes := []api.LeadNote{}
|
||||
for rows.Next() {
|
||||
n := api.LeadNote{}
|
||||
projectID := ""
|
||||
createdAt := ""
|
||||
updatedAt := ""
|
||||
err = rows.Scan(&n.Id, &n.Text, &createdAt, &updatedAt)
|
||||
err = rows.Scan(&n.Id, &n.Text, &projectID, &createdAt, &updatedAt)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if projectID != "" {
|
||||
n.ProjectId = &projectID
|
||||
}
|
||||
n.CreatedAt, err = parseTime(createdAt)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -39,21 +43,24 @@ func (s *Store) LeadNotes(ctx context.Context, owner string) ([]api.LeadNote, er
|
||||
return notes, rows.Err()
|
||||
}
|
||||
|
||||
func (s *Store) SaveLeadNote(ctx context.Context, owner, id, text, actor string) (api.LeadNote, error) {
|
||||
func (s *Store) SaveLeadNote(ctx context.Context, owner, id, projectID, text, actor string) (api.LeadNote, error) {
|
||||
at := time.Now().UTC()
|
||||
n := api.LeadNote{Id: id, Text: text, CreatedAt: at, UpdatedAt: at}
|
||||
if projectID != "" {
|
||||
n.ProjectId = &projectID
|
||||
}
|
||||
err := s.write(ctx, func(tx *sql.Tx, emit emitFunc) error {
|
||||
if id == "" {
|
||||
n.Id = NewID()
|
||||
_, err := tx.ExecContext(ctx, `INSERT INTO lead_notes (id, owner, text, created_at, updated_at) VALUES (?, ?, ?, ?, ?)`,
|
||||
n.Id, owner, text, formatTime(at), formatTime(at))
|
||||
_, err := tx.ExecContext(ctx, `INSERT INTO lead_notes (id, owner, text, project_id, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?)`,
|
||||
n.Id, owner, text, projectID, formatTime(at), formatTime(at))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
createdAt := ""
|
||||
err := tx.QueryRowContext(ctx, `UPDATE lead_notes SET text = ?, updated_at = ? WHERE id = ? AND owner = ? RETURNING created_at`,
|
||||
text, formatTime(at), id, owner).Scan(&createdAt)
|
||||
err := tx.QueryRowContext(ctx, `UPDATE lead_notes SET text = ?, project_id = ?, updated_at = ? WHERE id = ? AND owner = ? RETURNING created_at`,
|
||||
text, projectID, formatTime(at), id, owner).Scan(&createdAt)
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
return fmt.Errorf("note %s: %w", id, ErrNotFound)
|
||||
}
|
||||
|
||||
@@ -462,6 +462,8 @@ ALTER TABLE tasks ADD COLUMN merge_after_review_id TEXT NOT NULL DEFAULT '';
|
||||
ALTER TABLE tasks ADD COLUMN merge_after_review_target TEXT NOT NULL DEFAULT '';
|
||||
`}, {"add-inbox-response-images", `
|
||||
ALTER TABLE inbox_responses ADD COLUMN images TEXT NOT NULL DEFAULT '[]';
|
||||
`}, {"add-lead-note-projects", `
|
||||
ALTER TABLE lead_notes ADD COLUMN project_id TEXT NOT NULL DEFAULT '';
|
||||
`}}
|
||||
|
||||
const (
|
||||
|
||||
Vendored
+43
-19
@@ -7,6 +7,7 @@ const kinds = [
|
||||
{ key: "errors", label: "Session errors" },
|
||||
{ key: "rate_limits", label: "Rate limits" },
|
||||
];
|
||||
let notesProject = "";
|
||||
export function mountSettings(root) {
|
||||
const device = h("div", { class: "push-device" });
|
||||
const prefs = h("fieldset", { class: "push-prefs" });
|
||||
@@ -52,25 +53,9 @@ export function mountSettings(root) {
|
||||
replace(device, h("p", {}, "This browser doesn't show notifications."), button);
|
||||
};
|
||||
const notes = h("div", { class: "lead-notes" });
|
||||
let loads = 0;
|
||||
const loadNotes = async () => {
|
||||
const n = ++loads;
|
||||
let list;
|
||||
try {
|
||||
list = await get("/api/me/lead/notes");
|
||||
}
|
||||
catch (err) {
|
||||
replace(notes, h("p", { class: "hint" }, errorText(err)));
|
||||
return;
|
||||
}
|
||||
if (n !== loads) {
|
||||
return;
|
||||
}
|
||||
if (list.length === 0) {
|
||||
replace(notes, h("p", { class: "hint" }, "Your lead hasn't noted anything about how you work yet."));
|
||||
return;
|
||||
}
|
||||
replace(notes, h("ul", {}, ...list.map((note) => {
|
||||
let loads = 0;
|
||||
const noteItem = (note) => {
|
||||
const remove = h("button", {}, "delete");
|
||||
remove.onclick = async () => {
|
||||
if (!(await showConfirm([h("p", {}, "Delete this note? Your lead stops applying it."), h("p", {}, note.text)], "Delete", true, remove))) {
|
||||
@@ -84,11 +69,50 @@ export function mountSettings(root) {
|
||||
}
|
||||
};
|
||||
return h("li", {}, h("span", {}, note.text), remove);
|
||||
})));
|
||||
};
|
||||
const renderNotes = () => {
|
||||
if (!list) {
|
||||
return;
|
||||
}
|
||||
if (list.length === 0) {
|
||||
replace(notes, h("p", { class: "hint" }, "Your lead hasn't noted anything about how you work yet."));
|
||||
return;
|
||||
}
|
||||
const all = list;
|
||||
const scoped = (id) => all.filter((note) => (note.project_id ?? "") === id);
|
||||
const groups = [{ id: "", name: "Every project" }, ...state.projects.map((p) => ({ id: p.id, name: p.name }))];
|
||||
if (!groups.some((g) => g.id === notesProject)) {
|
||||
notesProject = "";
|
||||
}
|
||||
const picker = h("select", { title: "Which notes to show" }, ...groups.map((g) => h("option", { value: g.id }, `${g.name} (${scoped(g.id).length})`)));
|
||||
picker.value = notesProject;
|
||||
picker.onchange = () => {
|
||||
notesProject = picker.value;
|
||||
renderNotes();
|
||||
};
|
||||
const shown = scoped(notesProject);
|
||||
replace(notes, picker, shown.length === 0 ? h("p", { class: "hint" }, "No notes here yet.") : h("ul", {}, ...shown.map(noteItem)));
|
||||
};
|
||||
const loadNotes = async () => {
|
||||
const n = ++loads;
|
||||
let loaded;
|
||||
try {
|
||||
loaded = await get("/api/me/lead/notes");
|
||||
}
|
||||
catch (err) {
|
||||
replace(notes, h("p", { class: "hint" }, errorText(err)));
|
||||
return;
|
||||
}
|
||||
if (n !== loads) {
|
||||
return;
|
||||
}
|
||||
list = loaded;
|
||||
renderNotes();
|
||||
};
|
||||
replace(root, h("div", { class: "settings-view" }, h("h2", {}, "Notifications"), device, prefs, h("h2", {}, "What your lead has learned"), h("p", { class: "hint" }, "Your lead notes your priorities and preferences as it works with you, and starts every session with them."), notes));
|
||||
const offs = [
|
||||
on("me", renderPrefs),
|
||||
on("projects", renderNotes),
|
||||
on("event", (detail) => {
|
||||
const ev = detail;
|
||||
if (ev.kind === "lead.note_saved" || ev.kind === "lead.note_deleted") {
|
||||
|
||||
Vendored
+1
-1
File diff suppressed because one or more lines are too long
@@ -1433,6 +1433,8 @@ export interface components {
|
||||
LeadNote: {
|
||||
id: string;
|
||||
text: string;
|
||||
/** @description The project the note applies to; absent for a note that applies to every project. */
|
||||
project_id?: string;
|
||||
/** Format: date-time */
|
||||
created_at: string;
|
||||
/** Format: date-time */
|
||||
|
||||
+43
-25
@@ -9,6 +9,8 @@ const kinds: { key: keyof NotificationPrefs; label: string }[] = [
|
||||
{ key: "rate_limits", label: "Rate limits" },
|
||||
];
|
||||
|
||||
let notesProject = "";
|
||||
|
||||
export function mountSettings(root: HTMLElement): () => void {
|
||||
const device = h("div", { class: "push-device" });
|
||||
const prefs = h("fieldset", { class: "push-prefs" });
|
||||
@@ -61,29 +63,9 @@ export function mountSettings(root: HTMLElement): () => void {
|
||||
};
|
||||
|
||||
const notes = h("div", { class: "lead-notes" });
|
||||
let list: LeadNote[] | undefined;
|
||||
let loads = 0;
|
||||
const loadNotes = async () => {
|
||||
const n = ++loads;
|
||||
let list: LeadNote[];
|
||||
try {
|
||||
list = await get("/api/me/lead/notes");
|
||||
} catch (err) {
|
||||
replace(notes, h("p", { class: "hint" }, errorText(err)));
|
||||
return;
|
||||
}
|
||||
if (n !== loads) {
|
||||
return;
|
||||
}
|
||||
if (list.length === 0) {
|
||||
replace(notes, h("p", { class: "hint" }, "Your lead hasn't noted anything about how you work yet."));
|
||||
return;
|
||||
}
|
||||
replace(
|
||||
notes,
|
||||
h(
|
||||
"ul",
|
||||
{},
|
||||
...list.map((note) => {
|
||||
const noteItem = (note: LeadNote) => {
|
||||
const remove = h("button", {}, "delete");
|
||||
remove.onclick = async () => {
|
||||
if (!(await showConfirm([h("p", {}, "Delete this note? Your lead stops applying it."), h("p", {}, note.text)], "Delete", true, remove))) {
|
||||
@@ -96,9 +78,44 @@ export function mountSettings(root: HTMLElement): () => void {
|
||||
}
|
||||
};
|
||||
return h("li", {}, h("span", {}, note.text), remove);
|
||||
}),
|
||||
),
|
||||
);
|
||||
};
|
||||
const renderNotes = () => {
|
||||
if (!list) {
|
||||
return;
|
||||
}
|
||||
if (list.length === 0) {
|
||||
replace(notes, h("p", { class: "hint" }, "Your lead hasn't noted anything about how you work yet."));
|
||||
return;
|
||||
}
|
||||
const all = list;
|
||||
const scoped = (id: string) => all.filter((note) => (note.project_id ?? "") === id);
|
||||
const groups = [{ id: "", name: "Every project" }, ...state.projects.map((p) => ({ id: p.id, name: p.name }))];
|
||||
if (!groups.some((g) => g.id === notesProject)) {
|
||||
notesProject = "";
|
||||
}
|
||||
const picker = h("select", { title: "Which notes to show" }, ...groups.map((g) => h("option", { value: g.id }, `${g.name} (${scoped(g.id).length})`)));
|
||||
picker.value = notesProject;
|
||||
picker.onchange = () => {
|
||||
notesProject = picker.value;
|
||||
renderNotes();
|
||||
};
|
||||
const shown = scoped(notesProject);
|
||||
replace(notes, picker, shown.length === 0 ? h("p", { class: "hint" }, "No notes here yet.") : h("ul", {}, ...shown.map(noteItem)));
|
||||
};
|
||||
const loadNotes = async () => {
|
||||
const n = ++loads;
|
||||
let loaded: LeadNote[];
|
||||
try {
|
||||
loaded = await get("/api/me/lead/notes");
|
||||
} catch (err) {
|
||||
replace(notes, h("p", { class: "hint" }, errorText(err)));
|
||||
return;
|
||||
}
|
||||
if (n !== loads) {
|
||||
return;
|
||||
}
|
||||
list = loaded;
|
||||
renderNotes();
|
||||
};
|
||||
|
||||
replace(
|
||||
@@ -116,6 +133,7 @@ export function mountSettings(root: HTMLElement): () => void {
|
||||
);
|
||||
const offs = [
|
||||
on("me", renderPrefs),
|
||||
on("projects", renderNotes),
|
||||
on("event", (detail) => {
|
||||
const ev = detail as ByreEvent;
|
||||
if (ev.kind === "lead.note_saved" || ev.kind === "lead.note_deleted") {
|
||||
|
||||
+5
-10
@@ -1040,7 +1040,8 @@ button.compact.danger {
|
||||
.snap-row button,
|
||||
.todo-link .todo-action button,
|
||||
.finding-actions button,
|
||||
.note button.merge-after-review-cancel {
|
||||
.note button.merge-after-review-cancel,
|
||||
.lead-notes button {
|
||||
padding: 0 6px;
|
||||
border-radius: 4px;
|
||||
font-size: 11px;
|
||||
@@ -2418,15 +2419,9 @@ button.danger-primary {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.lead-notes button {
|
||||
padding: 0;
|
||||
border: none;
|
||||
background: none;
|
||||
color: var(--accent);
|
||||
}
|
||||
|
||||
.lead-notes button:hover {
|
||||
text-decoration: underline;
|
||||
.lead-notes select {
|
||||
width: auto;
|
||||
margin: 0 0 8px;
|
||||
}
|
||||
|
||||
.fatal {
|
||||
|
||||
Reference in New Issue
Block a user