-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathKnowledgeRootResolution.cs
More file actions
99 lines (82 loc) · 4.13 KB
/
Copy pathKnowledgeRootResolution.cs
File metadata and controls
99 lines (82 loc) · 4.13 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
namespace AgentNotes.Core;
/// <summary>Resolves knowledge repository roots from TOML (<c>--config</c>), tool args, or legacy inference.</summary>
public static class KnowledgeRootResolution
{
/// <summary>Resolve root for read operations (<c>read_knowledge_file</c>, <c>list_knowledge_files</c>).</summary>
public static string ResolveForRead(string? knowledgePath, string? knowledgeRootId) =>
Resolve(knowledgePath, knowledgeRootId, forWrite: false);
/// <summary>Resolve root for write operations; only <see cref="LocalSettings.PrimaryKnowledgeRoot"/> is writable when configured.</summary>
public static string ResolveForWrite(string? knowledgePath, string? knowledgeRootId) =>
Resolve(knowledgePath, knowledgeRootId, forWrite: true);
private static string Resolve(string? knowledgePath, string? knowledgeRootId, bool forWrite)
{
if (!string.IsNullOrWhiteSpace(knowledgePath) && !string.IsNullOrWhiteSpace(knowledgeRootId))
throw new ArgumentException("Specify either knowledge_path or knowledge_root_id, not both.");
if (!string.IsNullOrWhiteSpace(knowledgePath))
{
var path = Path.GetFullPath(knowledgePath.Trim());
if (forWrite)
EnsureWritable(path);
return path;
}
if (!string.IsNullOrWhiteSpace(knowledgeRootId))
return ResolveById(knowledgeRootId.Trim(), forWrite);
var fallback = NotesStorage.ResolveKnowledgeRootLegacy();
if (forWrite)
EnsureWritable(fallback);
return fallback;
}
private static string ResolveById(string id, bool forWrite)
{
id = id.Trim();
if (!AgentNotesRuntime.IsConfigured)
throw new ArgumentException(
"knowledge_root_id requires --config with [knowledge] and matching [[knowledge.read_only]] or [knowledge.roots].");
var settings = AgentNotesRuntime.Settings;
if (settings.KnowledgeRoots.TryGetValue(id, out var named))
{
if (forWrite)
EnsureWritable(named);
return named;
}
var readOnly = settings.ReadOnlyKnowledgeRoots
.FirstOrDefault(r => string.Equals(r.Id, id, StringComparison.OrdinalIgnoreCase));
if (readOnly is not null)
{
if (forWrite)
throw new InvalidOperationException(
$"Knowledge root '{readOnly.Id}' is read-only ({readOnly.Path}). Writes go to primary only.");
return readOnly.Path;
}
throw new ArgumentException(
$"Unknown knowledge_root_id '{id}'. Known: {FormatKnownRootIds(settings)}.");
}
private static void EnsureWritable(string resolvedRoot)
{
if (!AgentNotesRuntime.IsConfigured)
return;
var primary = AgentNotesRuntime.Settings.PrimaryKnowledgeRoot;
if (PathsEqual(resolvedRoot, primary))
return;
var readOnly = AgentNotesRuntime.Settings.ReadOnlyKnowledgeRoots
.FirstOrDefault(r => PathsEqual(r.Path, resolvedRoot));
if (readOnly is not null)
throw new InvalidOperationException(
$"Knowledge root '{readOnly.Id}' is read-only. Writes are allowed only on primary ({primary}).");
throw new InvalidOperationException(
$"Writes are allowed only on primary knowledge root ({primary}), not '{resolvedRoot}'.");
}
internal static bool PathsEqual(string a, string b) =>
string.Equals(
Path.GetFullPath(a.TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar)),
Path.GetFullPath(b.TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar)),
OperatingSystem.IsWindows() ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal);
private static string FormatKnownRootIds(LocalSettings settings)
{
var ids = settings.KnowledgeRoots.Keys
.Concat(settings.ReadOnlyKnowledgeRoots.Select(r => r.Id))
.Distinct(StringComparer.OrdinalIgnoreCase)
.OrderBy(static x => x, StringComparer.OrdinalIgnoreCase);
return string.Join(", ", ids);
}
}