@andespindola/brainlink 1.0.4 → 1.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +17 -9
- package/dist/application/add-note.js +2 -2
- package/dist/application/build-context.js +16 -10
- package/dist/application/canonical-context-links.js +44 -5
- package/dist/application/check-package-update.js +105 -0
- package/dist/application/frontend/client/chunk-fetch.js +236 -0
- package/dist/application/frontend/client/controls.js +178 -0
- package/dist/application/frontend/client/elements.js +122 -0
- package/dist/application/frontend/client/input.js +202 -0
- package/dist/application/frontend/client/node-details.js +191 -0
- package/dist/application/frontend/client/rendering.js +296 -0
- package/dist/application/frontend/client/scope-theme.js +114 -0
- package/dist/application/frontend/client/spatial.js +98 -0
- package/dist/application/frontend/client/storage.js +215 -0
- package/dist/application/frontend/client/upload.js +90 -0
- package/dist/application/frontend/client/worker-bootstrap.js +147 -0
- package/dist/application/frontend/client-js.js +24 -1837
- package/dist/application/frontend/client-render-worker-js.js +1 -1
- package/dist/application/index-vault-phases.js +189 -0
- package/dist/application/index-vault.js +44 -165
- package/dist/application/server/routes.js +12 -9
- package/dist/cli/commands/write/dedupe-commands.js +59 -0
- package/dist/cli/commands/write/index-commands.js +205 -0
- package/dist/cli/commands/write/link-commands.js +68 -0
- package/dist/cli/commands/write/note-commands.js +146 -0
- package/dist/cli/commands/write/server-commands.js +553 -0
- package/dist/cli/commands/write/shared.js +35 -0
- package/dist/cli/commands/write/vault-lifecycle-commands.js +270 -0
- package/dist/cli/commands/write-commands.js +12 -1303
- package/dist/cli/main.js +39 -3
- package/dist/domain/context.js +39 -3
- package/dist/domain/embeddings.js +31 -5
- package/dist/domain/graph-contexts.js +62 -57
- package/dist/domain/graph-layout/cauliflower-layout.js +116 -0
- package/dist/domain/graph-layout/collisions.js +100 -0
- package/dist/domain/graph-layout/hierarchy.js +135 -0
- package/dist/domain/graph-layout/metrics.js +111 -0
- package/dist/domain/graph-layout/segments.js +76 -0
- package/dist/domain/graph-layout/star-layout.js +110 -0
- package/dist/domain/graph-layout.js +4 -625
- package/dist/infrastructure/config.js +10 -4
- package/dist/infrastructure/file-index.js +13 -4
- package/dist/infrastructure/semantic-prefilter.js +24 -0
- package/dist/mcp/server.js +7 -0
- package/dist/mcp/tool-guard.js +29 -0
- package/dist/mcp/tools/maintenance-tools.js +409 -0
- package/dist/mcp/tools/read-tools.js +504 -0
- package/dist/mcp/tools/shared.js +216 -0
- package/dist/mcp/tools/write-tools.js +247 -0
- package/dist/mcp/tools.js +3 -1357
- package/docs/AGENT_USAGE.md +4 -4
- package/docs/QUICKSTART.md +5 -1
- package/package.json +2 -2
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
import { basename, extname } from 'node:path';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import { resolveAgentRuntimeDefaults, sanitizeContextStrategy, sanitizeSearchMode } from '../../infrastructure/config.js';
|
|
4
|
+
import { loadBrainlinkConfig } from '../../infrastructure/config.js';
|
|
5
|
+
import { assertVaultAllowed } from '../../infrastructure/file-system-vault.js';
|
|
6
|
+
import { indexVault } from '../../application/index-vault.js';
|
|
7
|
+
import { getBootstrapPolicy, getBootstrapSessionStatus, getContextSessionStatus, touchBootstrapSession } from '../../infrastructure/session-state.js';
|
|
8
|
+
export const positiveInteger = (fallback) => z
|
|
9
|
+
.number()
|
|
10
|
+
.int()
|
|
11
|
+
.positive()
|
|
12
|
+
.optional()
|
|
13
|
+
.transform((value) => value ?? fallback);
|
|
14
|
+
export const optionalPositiveInteger = () => z
|
|
15
|
+
.number()
|
|
16
|
+
.int()
|
|
17
|
+
.positive()
|
|
18
|
+
.optional();
|
|
19
|
+
export const vaultInput = {
|
|
20
|
+
vault: z.string().min(1).optional().describe('Vault directory. Omit to use the configured Brainlink default vault.')
|
|
21
|
+
};
|
|
22
|
+
export const agentInput = {
|
|
23
|
+
agent: z
|
|
24
|
+
.string()
|
|
25
|
+
.min(1)
|
|
26
|
+
.optional()
|
|
27
|
+
.describe('Agent memory namespace. Omit to use Brainlink.config defaultAgent, otherwise read all agent namespaces.')
|
|
28
|
+
};
|
|
29
|
+
export const searchModeInput = {
|
|
30
|
+
mode: z.enum(['fts', 'semantic', 'hybrid']).optional().describe('Search mode. Defaults to the Brainlink config value.')
|
|
31
|
+
};
|
|
32
|
+
export const contextStrategyInput = {
|
|
33
|
+
strategy: z
|
|
34
|
+
.enum(['rag', 'cag', 'auto'])
|
|
35
|
+
.optional()
|
|
36
|
+
.describe('Context strategy per call. Use rag for fresh retrieval assembly, cag to reuse persisted context packs when fresh, or auto to choose CAG on fresh pack hits and RAG otherwise. Defaults to the Brainlink config value.')
|
|
37
|
+
};
|
|
38
|
+
export const resolveExecutionContext = async (input) => {
|
|
39
|
+
const config = await loadBrainlinkConfig();
|
|
40
|
+
const vault = await assertVaultAllowed(input.vault ?? config.vault, config.allowedVaults);
|
|
41
|
+
const agent = input.agent ?? config.defaultAgent;
|
|
42
|
+
const defaults = resolveAgentRuntimeDefaults(config, agent);
|
|
43
|
+
return {
|
|
44
|
+
config,
|
|
45
|
+
vault,
|
|
46
|
+
agent,
|
|
47
|
+
defaults
|
|
48
|
+
};
|
|
49
|
+
};
|
|
50
|
+
export const inferTitleFromPath = (filePath) => {
|
|
51
|
+
const extension = extname(filePath);
|
|
52
|
+
const fromFileName = basename(filePath, extension);
|
|
53
|
+
return fromFileName
|
|
54
|
+
.trim()
|
|
55
|
+
.replace(/[-_]+/g, ' ')
|
|
56
|
+
.replace(/\s+/g, ' ')
|
|
57
|
+
.trim();
|
|
58
|
+
};
|
|
59
|
+
export const isTruthy = (value) => value !== false;
|
|
60
|
+
export const jsonResult = (value) => ({
|
|
61
|
+
content: [
|
|
62
|
+
{
|
|
63
|
+
type: 'text',
|
|
64
|
+
text: JSON.stringify(value, null, 2)
|
|
65
|
+
}
|
|
66
|
+
],
|
|
67
|
+
structuredContent: value
|
|
68
|
+
});
|
|
69
|
+
export const preflightResult = (value) => jsonResult({
|
|
70
|
+
preflightRequired: true,
|
|
71
|
+
...value
|
|
72
|
+
});
|
|
73
|
+
export const withNextActions = (value, nextActions) => ({
|
|
74
|
+
...value,
|
|
75
|
+
nextActions
|
|
76
|
+
});
|
|
77
|
+
export const ensureBootstrapReady = async (context, input, toolName) => {
|
|
78
|
+
const policy = await getBootstrapPolicy();
|
|
79
|
+
if (!policy.enforceBootstrap) {
|
|
80
|
+
return {
|
|
81
|
+
bootstrap: {
|
|
82
|
+
autoBootstrapped: false,
|
|
83
|
+
policy,
|
|
84
|
+
statusBefore: {
|
|
85
|
+
ready: true,
|
|
86
|
+
stale: false
|
|
87
|
+
},
|
|
88
|
+
reason: 'Bootstrap enforcement is disabled by policy.'
|
|
89
|
+
}
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
const status = await getBootstrapSessionStatus(context.vault, context.agent);
|
|
93
|
+
if (status.ready) {
|
|
94
|
+
return {
|
|
95
|
+
bootstrap: {
|
|
96
|
+
autoBootstrapped: false,
|
|
97
|
+
policy,
|
|
98
|
+
statusBefore: status,
|
|
99
|
+
reason: 'Bootstrap session is already fresh for this vault/agent.'
|
|
100
|
+
}
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
if (policy.autoBootstrapOnRead) {
|
|
104
|
+
const index = await indexVault(context.vault);
|
|
105
|
+
const session = await touchBootstrapSession(context.vault, context.agent);
|
|
106
|
+
const statusAfter = await getBootstrapSessionStatus(context.vault, context.agent);
|
|
107
|
+
return {
|
|
108
|
+
bootstrap: {
|
|
109
|
+
autoBootstrapped: true,
|
|
110
|
+
policy,
|
|
111
|
+
statusBefore: status,
|
|
112
|
+
statusAfter,
|
|
113
|
+
session,
|
|
114
|
+
index,
|
|
115
|
+
reason: 'Auto-bootstrap was applied for this read call because bootstrap was missing or stale.'
|
|
116
|
+
}
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
const mode = typeof input.mode === 'string' && ['fts', 'semantic', 'hybrid'].includes(input.mode) ? input.mode : 'hybrid';
|
|
120
|
+
const strategy = sanitizeContextStrategy(typeof input.strategy === 'string' ? input.strategy : undefined, 'rag');
|
|
121
|
+
const query = typeof input.query === 'string' && input.query.trim().length > 0 ? input.query : undefined;
|
|
122
|
+
const bootstrapArgs = {
|
|
123
|
+
vault: context.vault,
|
|
124
|
+
...(context.agent ? { agent: context.agent } : {}),
|
|
125
|
+
...(query ? { query } : {}),
|
|
126
|
+
mode,
|
|
127
|
+
strategy
|
|
128
|
+
};
|
|
129
|
+
const nextActions = [
|
|
130
|
+
{
|
|
131
|
+
tool: 'brainlink_bootstrap',
|
|
132
|
+
reason: 'Bootstrap is required before read tools so retrieval stays grounded in current vault state.',
|
|
133
|
+
args: bootstrapArgs
|
|
134
|
+
}
|
|
135
|
+
];
|
|
136
|
+
return {
|
|
137
|
+
preflight: preflightResult(withNextActions({
|
|
138
|
+
vault: context.vault,
|
|
139
|
+
agent: context.agent,
|
|
140
|
+
blockedTool: toolName,
|
|
141
|
+
policy,
|
|
142
|
+
bootstrapStatus: status,
|
|
143
|
+
guidance: 'Run brainlink_bootstrap first for this vault/agent before using read tools. This keeps retrieval grounded and memory state consistent.',
|
|
144
|
+
bootstrapArgs
|
|
145
|
+
}, nextActions))
|
|
146
|
+
};
|
|
147
|
+
};
|
|
148
|
+
export const ensureContextReady = async (context, input, toolName) => {
|
|
149
|
+
const policy = await getBootstrapPolicy();
|
|
150
|
+
if (!policy.enforceContextFirst) {
|
|
151
|
+
return {
|
|
152
|
+
context: {
|
|
153
|
+
policy,
|
|
154
|
+
statusBefore: {
|
|
155
|
+
ready: true,
|
|
156
|
+
stale: false
|
|
157
|
+
},
|
|
158
|
+
reason: 'Context-first enforcement is disabled by policy.'
|
|
159
|
+
}
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
const status = await getContextSessionStatus(context.vault, context.agent);
|
|
163
|
+
if (status.ready) {
|
|
164
|
+
return {
|
|
165
|
+
context: {
|
|
166
|
+
policy,
|
|
167
|
+
statusBefore: status,
|
|
168
|
+
reason: 'Context session is already fresh for this vault/agent.'
|
|
169
|
+
}
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
const queryFromInput = typeof input.query === 'string' && input.query.trim().length > 0
|
|
173
|
+
? input.query
|
|
174
|
+
: typeof input.contextQuery === 'string' && input.contextQuery.trim().length > 0
|
|
175
|
+
? input.contextQuery
|
|
176
|
+
: '<task>';
|
|
177
|
+
const mode = sanitizeSearchMode(typeof input.mode === 'string' ? input.mode : undefined, context.defaults.defaultSearchMode);
|
|
178
|
+
const strategy = sanitizeContextStrategy(typeof input.strategy === 'string' ? input.strategy : undefined, context.defaults.defaultContextStrategy);
|
|
179
|
+
const limit = typeof input.limit === 'number' && Number.isFinite(input.limit) && input.limit > 0
|
|
180
|
+
? input.limit
|
|
181
|
+
: typeof input.contextLimit === 'number' && Number.isFinite(input.contextLimit) && input.contextLimit > 0
|
|
182
|
+
? input.contextLimit
|
|
183
|
+
: context.defaults.defaultSearchLimit;
|
|
184
|
+
const tokens = typeof input.tokens === 'number' && Number.isFinite(input.tokens) && input.tokens > 0
|
|
185
|
+
? input.tokens
|
|
186
|
+
: typeof input.contextTokens === 'number' && Number.isFinite(input.contextTokens) && input.contextTokens > 0
|
|
187
|
+
? input.contextTokens
|
|
188
|
+
: context.defaults.defaultContextTokens;
|
|
189
|
+
const contextArgs = {
|
|
190
|
+
vault: context.vault,
|
|
191
|
+
...(context.agent ? { agent: context.agent } : {}),
|
|
192
|
+
query: queryFromInput,
|
|
193
|
+
mode,
|
|
194
|
+
strategy,
|
|
195
|
+
limit,
|
|
196
|
+
tokens
|
|
197
|
+
};
|
|
198
|
+
const nextActions = [
|
|
199
|
+
{
|
|
200
|
+
tool: 'brainlink_context',
|
|
201
|
+
reason: 'Context must be loaded first so Brainlink is the primary retrieval source before other read tools.',
|
|
202
|
+
args: contextArgs
|
|
203
|
+
}
|
|
204
|
+
];
|
|
205
|
+
return {
|
|
206
|
+
preflight: preflightResult(withNextActions({
|
|
207
|
+
vault: context.vault,
|
|
208
|
+
agent: context.agent,
|
|
209
|
+
blockedTool: toolName,
|
|
210
|
+
policy,
|
|
211
|
+
contextStatus: status,
|
|
212
|
+
guidance: 'Run brainlink_context first for this vault/agent before other read tools so answers are grounded on Brainlink context.',
|
|
213
|
+
contextArgs
|
|
214
|
+
}, nextActions))
|
|
215
|
+
};
|
|
216
|
+
};
|
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
import { readFile } from 'node:fs/promises';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import { addNoteWithMetadata } from '../../application/add-note.js';
|
|
4
|
+
import { deleteNote } from '../../application/delete-note.js';
|
|
5
|
+
import { scanDuplicateNotes } from '../../application/dedupe-notes.js';
|
|
6
|
+
import { addInboxItem, listInboxItems, processInboxItems } from '../../application/inbox.js';
|
|
7
|
+
import { indexVault } from '../../application/index-vault.js';
|
|
8
|
+
import { buildRememberSuggestion } from '../../application/memory-suggestions.js';
|
|
9
|
+
import { addVolatileMemory, clearVolatileMemory } from '../../infrastructure/volatile-memory.js';
|
|
10
|
+
import { agentInput, inferTitleFromPath, isTruthy, jsonResult, optionalPositiveInteger, positiveInteger, resolveExecutionContext, vaultInput } from './shared.js';
|
|
11
|
+
export const addNoteInputSchema = {
|
|
12
|
+
...vaultInput,
|
|
13
|
+
title: z.string().min(1).describe('Markdown note title.'),
|
|
14
|
+
content: z
|
|
15
|
+
.string()
|
|
16
|
+
.min(1)
|
|
17
|
+
.describe('Durable Markdown memory. Include explicit [[wiki links]] and #tags when the memory should be connected. Put priority markers near important links, for example priority: high, #important or #critical.'),
|
|
18
|
+
...agentInput,
|
|
19
|
+
allowSensitive: z.boolean().optional().default(false).describe('Allow content that looks like a secret.'),
|
|
20
|
+
autoIndex: z.boolean().optional().default(true).describe('Reindex vault after writing note.'),
|
|
21
|
+
autoContextLinks: z
|
|
22
|
+
.boolean()
|
|
23
|
+
.optional()
|
|
24
|
+
.describe('Automatically add canonical Context Links to the inferred visual context hub. Defaults to Brainlink config.')
|
|
25
|
+
};
|
|
26
|
+
export const rememberInputSchema = {
|
|
27
|
+
...vaultInput,
|
|
28
|
+
...agentInput,
|
|
29
|
+
title: z.string().min(1).optional().describe('Optional note title. When omitted, Brainlink infers it from content.'),
|
|
30
|
+
content: z.string().min(1).describe('Memory content to capture as a durable Markdown note.'),
|
|
31
|
+
tags: z.array(z.string()).optional().default([]).describe('Extra tags to include.'),
|
|
32
|
+
links: z.array(z.string()).optional().default([]).describe('Explicit Context Links to include.'),
|
|
33
|
+
linkLimit: positiveInteger(5).describe('Maximum suggested Context Links to include.'),
|
|
34
|
+
dryRun: z.boolean().optional().default(false).describe('Preview inferred note without writing it.'),
|
|
35
|
+
allowSensitive: z.boolean().optional().default(false).describe('Allow content that looks like a secret.'),
|
|
36
|
+
autoIndex: z.boolean().optional().default(true).describe('Reindex vault after writing note.')
|
|
37
|
+
};
|
|
38
|
+
export const inboxAddInputSchema = {
|
|
39
|
+
...vaultInput,
|
|
40
|
+
...agentInput,
|
|
41
|
+
content: z.string().min(1).describe('Quick memory content to store as an untriaged inbox note.'),
|
|
42
|
+
autoIndex: z.boolean().optional().default(true).describe('Reindex vault after writing inbox item.')
|
|
43
|
+
};
|
|
44
|
+
export const inboxListInputSchema = {
|
|
45
|
+
...vaultInput,
|
|
46
|
+
...agentInput,
|
|
47
|
+
limit: positiveInteger(20).describe('Maximum inbox items to return.')
|
|
48
|
+
};
|
|
49
|
+
export const inboxProcessInputSchema = {
|
|
50
|
+
...vaultInput,
|
|
51
|
+
...agentInput,
|
|
52
|
+
limit: positiveInteger(10).describe('Maximum inbox items to inspect.')
|
|
53
|
+
};
|
|
54
|
+
export const deleteNoteInputSchema = {
|
|
55
|
+
...vaultInput,
|
|
56
|
+
...agentInput,
|
|
57
|
+
title: z.string().min(1).optional().describe('Note title to delete. Use agent to disambiguate namespaced notes.'),
|
|
58
|
+
path: z.string().min(1).optional().describe('Vault-relative or absolute Markdown note path to delete.'),
|
|
59
|
+
confirm: z.boolean().describe('Must be true to confirm deletion.'),
|
|
60
|
+
autoIndex: z.boolean().optional().default(true).describe('Reindex vault after deletion. Defaults to true.')
|
|
61
|
+
};
|
|
62
|
+
export const volatileAddInputSchema = {
|
|
63
|
+
...vaultInput,
|
|
64
|
+
...agentInput,
|
|
65
|
+
content: z
|
|
66
|
+
.string()
|
|
67
|
+
.min(1)
|
|
68
|
+
.describe('Temporary agent-decided memory. Use for current task state, hypotheses, transient user preferences and unconfirmed findings.'),
|
|
69
|
+
ttlMinutes: optionalPositiveInteger().describe('Minutes before this volatile memory expires. Defaults to 240.'),
|
|
70
|
+
tags: z.array(z.string()).optional().default([]).describe('Optional tags for volatile retrieval.')
|
|
71
|
+
};
|
|
72
|
+
export const volatileClearInputSchema = {
|
|
73
|
+
...vaultInput,
|
|
74
|
+
...agentInput
|
|
75
|
+
};
|
|
76
|
+
export const addFileInputSchema = {
|
|
77
|
+
...vaultInput,
|
|
78
|
+
...agentInput,
|
|
79
|
+
title: z.string().min(1).optional().describe('Optional note title override. If omitted, uses file name.'),
|
|
80
|
+
filePath: z.string().min(1).describe('Filesystem path to markdown or text file to ingest.'),
|
|
81
|
+
autoIndex: z.boolean().optional().default(true).describe('Reindex vault after ingesting file.'),
|
|
82
|
+
allowSensitive: z.boolean().optional().default(false).describe('Allow content that looks like a secret.')
|
|
83
|
+
};
|
|
84
|
+
export const addNoteTool = async (input) => {
|
|
85
|
+
const context = await resolveExecutionContext(input);
|
|
86
|
+
const shouldIndex = isTruthy(input.autoIndex);
|
|
87
|
+
const added = await addNoteWithMetadata(context.vault, input.title, input.content, context.agent, {
|
|
88
|
+
allowSensitive: input.allowSensitive,
|
|
89
|
+
autoContextLinks: input.autoContextLinks ?? context.config.autoCanonicalContextLinks
|
|
90
|
+
});
|
|
91
|
+
const index = shouldIndex ? await indexVault(context.vault) : undefined;
|
|
92
|
+
const focusPath = added.path.includes('agents/') ? added.path.slice(added.path.indexOf('agents/')).replaceAll('\\', '/') : undefined;
|
|
93
|
+
const possibleDuplicates = await scanDuplicateNotes(context.vault, {
|
|
94
|
+
agentId: context.agent,
|
|
95
|
+
focusPath,
|
|
96
|
+
limit: 5,
|
|
97
|
+
minSemanticScore: 0.92,
|
|
98
|
+
includeSemantic: true
|
|
99
|
+
});
|
|
100
|
+
return jsonResult({
|
|
101
|
+
vault: context.vault,
|
|
102
|
+
title: input.title,
|
|
103
|
+
agent: context.agent,
|
|
104
|
+
path: added.path,
|
|
105
|
+
writeConnectivity: {
|
|
106
|
+
autoLinked: added.autoLinked,
|
|
107
|
+
linkTarget: added.linkTarget,
|
|
108
|
+
context: added.context,
|
|
109
|
+
hubCreated: added.hubCreated,
|
|
110
|
+
guaranteedEdge: added.autoLinked
|
|
111
|
+
},
|
|
112
|
+
possibleDuplicates,
|
|
113
|
+
...(index ? { index } : {})
|
|
114
|
+
});
|
|
115
|
+
};
|
|
116
|
+
export const rememberTool = async (input) => {
|
|
117
|
+
const context = await resolveExecutionContext(input);
|
|
118
|
+
const suggestion = await buildRememberSuggestion({
|
|
119
|
+
vaultPath: context.vault,
|
|
120
|
+
content: input.content,
|
|
121
|
+
agentId: context.agent,
|
|
122
|
+
title: input.title,
|
|
123
|
+
tags: input.tags,
|
|
124
|
+
links: input.links,
|
|
125
|
+
linkLimit: input.linkLimit
|
|
126
|
+
});
|
|
127
|
+
if (input.dryRun) {
|
|
128
|
+
return jsonResult({
|
|
129
|
+
dryRun: true,
|
|
130
|
+
vault: context.vault,
|
|
131
|
+
agent: context.agent,
|
|
132
|
+
suggestion
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
const added = await addNoteWithMetadata(context.vault, suggestion.title, suggestion.content, context.agent, {
|
|
136
|
+
allowSensitive: input.allowSensitive,
|
|
137
|
+
autoContextLinks: false
|
|
138
|
+
});
|
|
139
|
+
const index = input.autoIndex ? await indexVault(context.vault) : undefined;
|
|
140
|
+
return jsonResult({
|
|
141
|
+
dryRun: false,
|
|
142
|
+
vault: context.vault,
|
|
143
|
+
agent: context.agent,
|
|
144
|
+
suggestion,
|
|
145
|
+
path: added.path,
|
|
146
|
+
...(index ? { index } : {})
|
|
147
|
+
});
|
|
148
|
+
};
|
|
149
|
+
export const inboxAddTool = async (input) => {
|
|
150
|
+
const context = await resolveExecutionContext(input);
|
|
151
|
+
const result = await addInboxItem({
|
|
152
|
+
vaultPath: context.vault,
|
|
153
|
+
content: input.content,
|
|
154
|
+
agentId: context.agent,
|
|
155
|
+
autoIndex: input.autoIndex
|
|
156
|
+
});
|
|
157
|
+
return jsonResult({
|
|
158
|
+
vault: context.vault,
|
|
159
|
+
agent: context.agent,
|
|
160
|
+
...result
|
|
161
|
+
});
|
|
162
|
+
};
|
|
163
|
+
export const inboxListTool = async (input) => {
|
|
164
|
+
const context = await resolveExecutionContext(input);
|
|
165
|
+
const items = await listInboxItems(context.vault, input.limit);
|
|
166
|
+
return jsonResult({
|
|
167
|
+
vault: context.vault,
|
|
168
|
+
agent: context.agent,
|
|
169
|
+
items
|
|
170
|
+
});
|
|
171
|
+
};
|
|
172
|
+
export const inboxProcessTool = async (input) => {
|
|
173
|
+
const context = await resolveExecutionContext(input);
|
|
174
|
+
const items = await processInboxItems({
|
|
175
|
+
vaultPath: context.vault,
|
|
176
|
+
agentId: context.agent,
|
|
177
|
+
limit: input.limit
|
|
178
|
+
});
|
|
179
|
+
return jsonResult({
|
|
180
|
+
vault: context.vault,
|
|
181
|
+
agent: context.agent,
|
|
182
|
+
items
|
|
183
|
+
});
|
|
184
|
+
};
|
|
185
|
+
export const deleteNoteTool = async (input) => {
|
|
186
|
+
const context = await resolveExecutionContext(input);
|
|
187
|
+
const result = await deleteNote(context.vault, {
|
|
188
|
+
title: input.title,
|
|
189
|
+
path: input.path,
|
|
190
|
+
agentId: context.agent,
|
|
191
|
+
confirm: input.confirm,
|
|
192
|
+
autoIndex: input.autoIndex
|
|
193
|
+
});
|
|
194
|
+
return jsonResult({
|
|
195
|
+
vault: context.vault,
|
|
196
|
+
...result
|
|
197
|
+
});
|
|
198
|
+
};
|
|
199
|
+
export const volatileAddTool = async (input) => {
|
|
200
|
+
const context = await resolveExecutionContext(input);
|
|
201
|
+
const entry = await addVolatileMemory(context.vault, input.content, context.agent ?? 'shared', input.ttlMinutes ?? 240, input.tags);
|
|
202
|
+
return jsonResult({
|
|
203
|
+
vault: context.vault,
|
|
204
|
+
agent: context.agent,
|
|
205
|
+
volatile: true,
|
|
206
|
+
entry
|
|
207
|
+
});
|
|
208
|
+
};
|
|
209
|
+
export const volatileClearTool = async (input) => {
|
|
210
|
+
const context = await resolveExecutionContext(input);
|
|
211
|
+
const cleared = await clearVolatileMemory(context.vault, context.agent);
|
|
212
|
+
return jsonResult({
|
|
213
|
+
vault: context.vault,
|
|
214
|
+
agent: context.agent,
|
|
215
|
+
cleared
|
|
216
|
+
});
|
|
217
|
+
};
|
|
218
|
+
export const addFileTool = async (input) => {
|
|
219
|
+
const context = await resolveExecutionContext(input);
|
|
220
|
+
const content = await readFile(input.filePath, 'utf8');
|
|
221
|
+
const inferredTitle = inferTitleFromPath(input.filePath);
|
|
222
|
+
const title = input.title ?? inferredTitle;
|
|
223
|
+
if (title == null || title.length === 0) {
|
|
224
|
+
throw new Error('Cannot infer note title from file path. Provide a title explicitly.');
|
|
225
|
+
}
|
|
226
|
+
const shouldIndex = isTruthy(input.autoIndex);
|
|
227
|
+
const added = await addNoteWithMetadata(context.vault, title, content, context.agent, {
|
|
228
|
+
allowSensitive: input.allowSensitive,
|
|
229
|
+
autoContextLinks: context.config.autoCanonicalContextLinks
|
|
230
|
+
});
|
|
231
|
+
const index = shouldIndex ? await indexVault(context.vault) : undefined;
|
|
232
|
+
return jsonResult({
|
|
233
|
+
vault: context.vault,
|
|
234
|
+
title,
|
|
235
|
+
agent: context.agent,
|
|
236
|
+
filePath: input.filePath,
|
|
237
|
+
path: added.path,
|
|
238
|
+
writeConnectivity: {
|
|
239
|
+
autoLinked: added.autoLinked,
|
|
240
|
+
linkTarget: added.linkTarget,
|
|
241
|
+
context: added.context,
|
|
242
|
+
hubCreated: added.hubCreated,
|
|
243
|
+
guaranteedEdge: added.autoLinked
|
|
244
|
+
},
|
|
245
|
+
...(index ? { index } : {})
|
|
246
|
+
});
|
|
247
|
+
};
|