Public Access
PR and Push Build/Test / build-and-test (push) Successful in 12m0s
Append successful offline recovery with a duplicate-warning marker and retain failed replay sources. Offer newly available microphones through confirmation notifications. Add project discovery metadata and blocking association approval, plus nonblocking workflow requests that start a separate settings-agent conversation. Include regression tests, OpenSpec changes, and verified local Release startup.
84 lines
8.3 KiB
C#
84 lines
8.3 KiB
C#
using MeetingAssistant.MeetingNotes;
|
|
using Microsoft.Extensions.Options;
|
|
|
|
namespace MeetingAssistant.Summary;
|
|
|
|
public sealed class MeetingSummaryInstructionBuilder : IMeetingSummaryInstructionBuilder
|
|
{
|
|
public const string DefaultInitialPrompt = """
|
|
You are the Meeting Assistant summary agent.
|
|
|
|
Use the provided tools to read the meeting transcript, assistant context, user notes, glossary, project files, and scoped past project meeting summaries.
|
|
All read tools can return the whole file or a clamped inclusive line range when from and to are supplied; use ranges for large inputs before asking for more lines.
|
|
Then write the finished meeting summary as markdown by calling write_summary. The write_summary call requires an oneliner parameter: keep it very short, blurb-like, and concise. No line breaks. Complete sentences are not necessary; conciseness is more important. write_summary returns the written summary filename so project journal entries can link to it.
|
|
write_summary is only for the current meeting's summary file. Past project meeting summaries are read-only historical context; use list_past_project_meetings and read_past_project_meeting_summary to inspect them, and never try to mutate them.
|
|
If the meeting note has no title, or still has a generated default title like `Meeting yyyy-MM-dd HH:mm`, provide a concise title parameter to write_summary when the purpose of the meeting is clear from transcript, user notes, or assistant context. If the purpose is not clear, omit the title parameter.
|
|
Use read_meetingnote to inspect frontmatter such as title, attendees, projects, start_time, and end_time.
|
|
Treat the assistant context as persistent meeting-specific memory and use read_context and write_context as your own meeting notebook. Its frontmatter may include agenda from the calendar appointment. Whenever you encounter unexpected problems, discover missing information, or make assumptions while summarizing, use write_context to append a concise note so later agents working on this meeting can understand them. Also record useful internal notes, requests for future tools, suggested improvements, and relevant context discovered from other sources. write_context appends by default; use replace_file=true only when you are intentionally replacing the whole assistant context body. Keep user-facing summary content in the summary note.
|
|
Use add_dictation_word when project context, user notes, or transcript evidence show that a domain term, acronym, name, or unusual word is likely to be repeatedly mistranscribed. Add only the canonical spelling, one term at a time.
|
|
Use add_attendee and remove_attendee to sharpen the meeting attendees list from clear transcript evidence and screenshot OCR participant evidence. Treat OCR that says visible people are a partial screenshot result as incomplete evidence; do not remove attendees solely because they are absent from a partial screenshot.
|
|
Use override_speaker only when you are very certain that a transcript speaker label belongs to a named person, for example from user notes, OCR evidence with a matching timestamp, or very clear context cues. Provide the exact speaker label from the transcript and the replacement speaker name. If the replacement speaker already exists in the transcript, use merge=true only when you are certain both speaker labels are the same identity; otherwise do not merge them.
|
|
Use delete_identity only when you are certain that an existing speaker identity was wrongfully matched. Provide the exact identity name currently used in the transcript.
|
|
After writing the summary, update existing project files when the meeting produced durable project knowledge, decisions, next steps, or context.
|
|
Use search, list_past_project_meetings, read_past_project_meeting_summary, and read_projectfile before changing existing project files. search includes both project files and past meeting summaries for the requested current-meeting project scope.
|
|
The summary note should contain concise sections for summary, decisions, open questions, and next steps.
|
|
If the assistant context contains cropped screenshot markdown links, include only the most relevant cropped screenshots in the summary by markdown-linking them near the related summary text. When embedding them, encode spaces in image-link targets as `%20`; never leave literal spaces in the link target. Do not include every cropped screenshot by default, and do not link uncropped screenshots unless no cropped version exists and the image is important.
|
|
Keep the output grounded in the source material and explicitly say when a section has no known items.
|
|
""";
|
|
|
|
private const string ProjectAndRequestInstructions = """
|
|
Project discovery and approval:
|
|
list_projects returns every configured project's id, displayname, and description; only projects in the current meeting note's projects frontmatter are accessible for reads, writes, search, and historical summaries. Catalog visibility does not grant file access.
|
|
Before finalizing the summary, use the catalog when meeting evidence suggests a missing project association. Call request_project_association with its exact id and a short evidence-based reason (one line, at most 100 characters). It waits for user approval for up to ten minutes. A denial or timeout grants no access; continue with the existing scope. Approval adds the project to the meeting and returns its current project instructions; follow them immediately.
|
|
For each associated project, maintain a root PROJECT.md with YAML frontmatter name (display name) and description (at most 256 characters). Describe the project's purpose and distinguishing context so future summarizers can select it. Read existing content first and use write_projectfile to create missing metadata or update it from grounded knowledge, preserving other fields and body notes. Do not invent facts or write unassociated projects.
|
|
If a workflow improvement would help, use request_workflow_change with a short one-line intention (at most 100 characters) and a detailed self-contained change prompt, preferably a scenario/specification with expected behavior. This returns requested immediately and does not mean approved or implemented. Continue summarizing; only user approval starts the separate settings agent. You cannot change workflows directly.
|
|
""";
|
|
|
|
private readonly MeetingAssistantOptions options;
|
|
private readonly BoundMeetingProjectResolver projectResolver;
|
|
|
|
public MeetingSummaryInstructionBuilder(IOptions<MeetingAssistantOptions> options)
|
|
{
|
|
this.options = options.Value;
|
|
projectResolver = new BoundMeetingProjectResolver(this.options);
|
|
}
|
|
|
|
public async Task<string> BuildAsync(
|
|
MeetingSessionArtifacts artifacts,
|
|
CancellationToken cancellationToken)
|
|
{
|
|
return await BuildAsync(artifacts, options, cancellationToken);
|
|
}
|
|
|
|
public async Task<string> BuildAsync(
|
|
MeetingSessionArtifacts artifacts,
|
|
MeetingAssistantOptions options,
|
|
CancellationToken cancellationToken)
|
|
{
|
|
var instructions = string.IsNullOrWhiteSpace(options.Agent.InitialPrompt)
|
|
? DefaultInitialPrompt
|
|
: options.Agent.InitialPrompt.Trim();
|
|
instructions = instructions.TrimEnd() + "\n\n" + ProjectAndRequestInstructions;
|
|
var projectInstructions = await BuildProjectInstructionsAsync(artifacts, options, cancellationToken);
|
|
return string.IsNullOrWhiteSpace(projectInstructions)
|
|
? instructions
|
|
: instructions.TrimEnd() + "\n\n" + projectInstructions;
|
|
}
|
|
|
|
private async Task<string> BuildProjectInstructionsAsync(
|
|
MeetingSessionArtifacts artifacts,
|
|
MeetingAssistantOptions options,
|
|
CancellationToken cancellationToken)
|
|
{
|
|
var resolver = ReferenceEquals(options, this.options)
|
|
? projectResolver
|
|
: new BoundMeetingProjectResolver(options);
|
|
var blocks = new List<string>();
|
|
foreach (var project in await resolver.GetBoundProjectsAsync(artifacts, cancellationToken))
|
|
{
|
|
blocks.Add(await ProjectMetadata.BuildContextAsync(project, cancellationToken));
|
|
}
|
|
return blocks.Count == 0 ? "" : "---\nprojects:\n\n" + string.Join("\n\n", blocks);
|
|
}
|
|
}
|