Public Access
Make agent file writes append by default
PR and Push Build/Test / build-and-test (push) Failing after 6m36s
PR and Push Build/Test / build-and-test (push) Failing after 6m36s
This commit is contained in:
@@ -13,7 +13,7 @@ public sealed class MeetingSummaryInstructionBuilder : IMeetingSummaryInstructio
|
||||
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.
|
||||
If the meeting note has no title, provide a concise title parameter to write_summary.
|
||||
Use read_meetingnote to inspect frontmatter such as title, attendees, projects, start_time, and end_time.
|
||||
Use read_context and write_context as your own meeting notebook. Its frontmatter may include agenda from the calendar appointment. Record useful internal notes, missing context, requests for future tools, suggested improvements, and relevant context discovered from other sources. Keep user-facing summary content in the summary note.
|
||||
Use read_context and write_context as your own meeting notebook. Its frontmatter may include agenda from the calendar appointment. Record useful internal notes, missing context, 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.
|
||||
|
||||
@@ -249,12 +249,13 @@ public sealed class MeetingSummaryTools
|
||||
string content,
|
||||
int? @from = null,
|
||||
int? to = null,
|
||||
int? insert = null)
|
||||
int? insert = null,
|
||||
bool replace_file = false)
|
||||
{
|
||||
var editMode = FileLineEditMode.Create(@from, to, insert);
|
||||
var editMode = FileLineEditMode.Create(@from, to, insert, replace_file);
|
||||
if (editMode is null)
|
||||
{
|
||||
return "Refused: supply either both from and to for replacement, insert for insertion, or no line arguments for overwrite.";
|
||||
return "Refused: supply either both from and to for replacement, insert for insertion, or no line arguments for append; set replace_file=true only for whole-file replacement.";
|
||||
}
|
||||
|
||||
if (!File.Exists(artifacts.AssistantContextPath))
|
||||
@@ -316,12 +317,13 @@ public sealed class MeetingSummaryTools
|
||||
string content,
|
||||
int? @from = null,
|
||||
int? to = null,
|
||||
int? insert = null)
|
||||
int? insert = null,
|
||||
bool replace_file = false)
|
||||
{
|
||||
var editMode = FileLineEditMode.Create(@from, to, insert);
|
||||
var editMode = FileLineEditMode.Create(@from, to, insert, replace_file);
|
||||
if (editMode is null)
|
||||
{
|
||||
return "Refused: supply either both from and to for replacement, insert for insertion, or no line arguments for overwrite.";
|
||||
return "Refused: supply either both from and to for replacement, insert for insertion, or no line arguments for append; set replace_file=true only for whole-file replacement.";
|
||||
}
|
||||
|
||||
var target = ResolveExistingProjectFilePath(project, path);
|
||||
@@ -544,6 +546,15 @@ public sealed class MeetingSummaryTools
|
||||
return;
|
||||
}
|
||||
|
||||
if (editMode.Kind == FileLineEditKind.Append)
|
||||
{
|
||||
var existing = File.Exists(filePath)
|
||||
? await File.ReadAllTextAsync(filePath)
|
||||
: "";
|
||||
await File.WriteAllTextAsync(filePath, AppendContent(existing, content));
|
||||
return;
|
||||
}
|
||||
|
||||
var lines = File.Exists(filePath)
|
||||
? (await File.ReadAllLinesAsync(filePath)).ToList()
|
||||
: [];
|
||||
@@ -616,6 +627,11 @@ public sealed class MeetingSummaryTools
|
||||
return replacementContent;
|
||||
}
|
||||
|
||||
if (editMode.Kind == FileLineEditKind.Append)
|
||||
{
|
||||
return AppendContent(existingContent, replacementContent);
|
||||
}
|
||||
|
||||
var lines = SplitLines(existingContent);
|
||||
var replacementLines = SplitLines(replacementContent);
|
||||
if (editMode.Kind == FileLineEditKind.Insert)
|
||||
@@ -637,6 +653,24 @@ public sealed class MeetingSummaryTools
|
||||
return string.Join('\n', lines);
|
||||
}
|
||||
|
||||
private static string AppendContent(string existingContent, string content)
|
||||
{
|
||||
if (string.IsNullOrEmpty(existingContent))
|
||||
{
|
||||
return content;
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(content))
|
||||
{
|
||||
return existingContent;
|
||||
}
|
||||
|
||||
var separator = existingContent.EndsWith('\n') || existingContent.EndsWith('\r')
|
||||
? ""
|
||||
: "\n";
|
||||
return existingContent + separator + content;
|
||||
}
|
||||
|
||||
private Task<List<BoundMeetingProject>> GetBoundProjectsAsync()
|
||||
{
|
||||
return projectResolver.GetBoundProjectsAsync(artifacts);
|
||||
@@ -778,8 +812,13 @@ public sealed class MeetingSummaryTools
|
||||
int? To = null,
|
||||
int? Insert = null)
|
||||
{
|
||||
public static FileLineEditMode? Create(int? from, int? to, int? insert)
|
||||
public static FileLineEditMode? Create(int? from, int? to, int? insert, bool replaceFile)
|
||||
{
|
||||
if (replaceFile && (from.HasValue || to.HasValue || insert.HasValue))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (insert.HasValue)
|
||||
{
|
||||
return from.HasValue || to.HasValue
|
||||
@@ -794,12 +833,13 @@ public sealed class MeetingSummaryTools
|
||||
: null;
|
||||
}
|
||||
|
||||
return new FileLineEditMode(FileLineEditKind.Overwrite);
|
||||
return new FileLineEditMode(replaceFile ? FileLineEditKind.Overwrite : FileLineEditKind.Append);
|
||||
}
|
||||
}
|
||||
|
||||
private enum FileLineEditKind
|
||||
{
|
||||
Append,
|
||||
Overwrite,
|
||||
Replace,
|
||||
Insert
|
||||
|
||||
@@ -143,7 +143,7 @@ public sealed class OpenAiMeetingSummaryAgentPipeline : IMeetingSummaryPipeline
|
||||
AIFunctionFactory.Create(
|
||||
tools.WriteContext,
|
||||
"write_context",
|
||||
"Write internal assistant notes to the assistant context body. With no line arguments, overwrite the body. With from and to, replace that inclusive 1-based line range. With insert, insert content at that 1-based line position."),
|
||||
"Write internal assistant notes to the assistant context body. With no line arguments, append to the body. Set replace_file=true only when intentionally replacing the whole body. With from and to, replace that inclusive 1-based line range. With insert, insert content at that 1-based line position."),
|
||||
AIFunctionFactory.Create(
|
||||
tools.ReadGlossary,
|
||||
"read_glossary",
|
||||
@@ -187,7 +187,7 @@ public sealed class OpenAiMeetingSummaryAgentPipeline : IMeetingSummaryPipeline
|
||||
AIFunctionFactory.Create(
|
||||
tools.WriteProjectFile,
|
||||
"write_projectfile",
|
||||
"Write a file inside an existing project folder. With no line arguments, overwrite or create the file. With from and to, replace that inclusive 1-based line range. With insert, insert content at that 1-based line position."),
|
||||
"Write a file inside an existing project folder. With no line arguments, append or create the file. Set replace_file=true only when intentionally replacing the whole file. With from and to, replace that inclusive 1-based line range. With insert, insert content at that 1-based line position."),
|
||||
AIFunctionFactory.Create(
|
||||
tools.Search,
|
||||
"search",
|
||||
|
||||
@@ -124,7 +124,7 @@ public sealed class WorkflowRulesEditorChatPipeline : IWorkflowRulesEditorChatPi
|
||||
AIFunctionFactory.Create(
|
||||
tools.WriteRules,
|
||||
"write_rules",
|
||||
"Overwrite the configured workflow rules YAML file after validating that it parses as a workflow rules document."),
|
||||
"Append to the configured workflow rules YAML file by default after validating that the complete file parses as a workflow rules document. Set replace_file=true only when intentionally replacing the whole rules file."),
|
||||
AIFunctionFactory.Create(
|
||||
tools.Search,
|
||||
"search",
|
||||
|
||||
@@ -15,6 +15,7 @@ public sealed class WorkflowRulesEditorInstructionBuilder : IWorkflowRulesEditor
|
||||
Your purpose is to help edit the configured local workflow rules YAML file and manage the local speaker identity database for Meeting Assistant.
|
||||
Use the read_rules, search, and write_rules tools for workflow rules. Do not ask for or modify unrelated files.
|
||||
Read the existing rules before making changes unless the user explicitly asks to replace the whole file.
|
||||
write_rules appends by default. Pass replace_file=true only when you intentionally replace the complete rules file.
|
||||
Preserve valid YAML, keep personal/local rules in the configured ignored rules file, and keep changes minimal.
|
||||
When writing rules, prefer existing rule style and names.
|
||||
Use search_identities and read_identity before changing identities unless the user gives an exact identity id.
|
||||
|
||||
@@ -51,7 +51,7 @@ public sealed class WorkflowRulesEditorTools
|
||||
return ReadLines(await File.ReadAllTextAsync(rulesPath), from, to);
|
||||
}
|
||||
|
||||
public async Task<string> WriteRules(string yaml)
|
||||
public async Task<string> WriteRules(string yaml, bool replace_file = false)
|
||||
{
|
||||
if (rulesPath is null)
|
||||
{
|
||||
@@ -63,14 +63,20 @@ public sealed class WorkflowRulesEditorTools
|
||||
return "Refused: yaml must not be null.";
|
||||
}
|
||||
|
||||
var validation = ValidateYaml(yaml);
|
||||
var existing = File.Exists(rulesPath)
|
||||
? await File.ReadAllTextAsync(rulesPath)
|
||||
: "";
|
||||
var updated = replace_file
|
||||
? yaml
|
||||
: AppendContent(existing, yaml);
|
||||
var validation = ValidateYaml(updated);
|
||||
if (validation is not null)
|
||||
{
|
||||
return validation;
|
||||
}
|
||||
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(rulesPath)!);
|
||||
await File.WriteAllTextAsync(rulesPath, yaml);
|
||||
await File.WriteAllTextAsync(rulesPath, updated);
|
||||
return rulesPath;
|
||||
}
|
||||
|
||||
@@ -359,6 +365,24 @@ public sealed class WorkflowRulesEditorTools
|
||||
}
|
||||
}
|
||||
|
||||
private static string AppendContent(string existingContent, string content)
|
||||
{
|
||||
if (string.IsNullOrEmpty(existingContent))
|
||||
{
|
||||
return content;
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(content))
|
||||
{
|
||||
return existingContent;
|
||||
}
|
||||
|
||||
var separator = existingContent.EndsWith('\n') || existingContent.EndsWith('\r')
|
||||
? ""
|
||||
: "\n";
|
||||
return existingContent + separator + content;
|
||||
}
|
||||
|
||||
private static async Task<string?> RunRipgrepAsync(string rulesPath, string keywords)
|
||||
{
|
||||
try
|
||||
|
||||
Reference in New Issue
Block a user