@agent-native/core 0.111.3 → 0.112.0
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/corpus/README.md +2 -2
- package/corpus/core/CHANGELOG.md +18 -0
- package/corpus/core/package.json +3 -1
- package/corpus/core/src/cli/code-agent-executor.ts +17 -5
- package/corpus/core/src/cli/code-agent-runs.ts +43 -2
- package/corpus/core/src/cli/migration-codemod.ts +59 -9
- package/corpus/core/src/cli/multi-frontier-runs.ts +841 -0
- package/corpus/core/src/coding-tools/index.ts +27 -8
- package/corpus/core/src/embeddings/index.ts +233 -0
- package/corpus/core/src/index.ts +2 -0
- package/corpus/core/src/search/index.ts +413 -0
- package/corpus/templates/brain/.agents/skills/brain/RUNBOOK.md +48 -5
- package/corpus/templates/brain/.agents/skills/brain/SKILL.md +46 -14
- package/corpus/templates/brain/README.md +34 -3
- package/corpus/templates/brain/actions/approve-proposal.ts +2 -0
- package/corpus/templates/brain/actions/claim-distillation.ts +24 -27
- package/corpus/templates/brain/actions/create-source.ts +1 -1
- package/corpus/templates/brain/actions/get-knowledge.ts +6 -2
- package/corpus/templates/brain/actions/get-pilot-report.ts +44 -8
- package/corpus/templates/brain/actions/import-capture.ts +21 -9
- package/corpus/templates/brain/actions/import-transcript.ts +42 -14
- package/corpus/templates/brain/actions/list-captures.ts +10 -1
- package/corpus/templates/brain/actions/list-distillation-queue.ts +39 -14
- package/corpus/templates/brain/actions/list-projects.ts +51 -0
- package/corpus/templates/brain/actions/list-proposals.ts +80 -4
- package/corpus/templates/brain/actions/list-sources.ts +17 -3
- package/corpus/templates/brain/actions/manage-project.ts +118 -0
- package/corpus/templates/brain/actions/mark-capture-distilled.ts +68 -20
- package/corpus/templates/brain/actions/navigate.ts +1 -0
- package/corpus/templates/brain/actions/reject-proposal.ts +6 -1
- package/corpus/templates/brain/actions/resanitize-captures.ts +159 -33
- package/corpus/templates/brain/actions/review-proposal.ts +2 -0
- package/corpus/templates/brain/actions/search-everything.ts +10 -1
- package/corpus/templates/brain/actions/search-knowledge.ts +1 -1
- package/corpus/templates/brain/actions/set-settings.ts +36 -1
- package/corpus/templates/brain/actions/update-proposal.ts +2 -0
- package/corpus/templates/brain/app/hooks/use-distillation-bridge.ts +18 -9
- package/corpus/templates/brain/app/hooks/use-navigation-state.ts +3 -0
- package/corpus/templates/brain/app/i18n-data.ts +731 -2
- package/corpus/templates/brain/app/lib/brain.ts +33 -1
- package/corpus/templates/brain/app/routes/ops.tsx +47 -0
- package/corpus/templates/brain/app/routes/review.tsx +41 -2
- package/corpus/templates/brain/app/routes/search.tsx +33 -2
- package/corpus/templates/brain/app/routes/settings.tsx +158 -0
- package/corpus/templates/brain/app/routes/sources.tsx +31 -0
- package/corpus/templates/brain/changelog/2026-07-19-added-private-source-safe-semantic-search.md +6 -0
- package/corpus/templates/brain/jobs/process-ingest-queue.ts +270 -36
- package/corpus/templates/brain/package.json +1 -0
- package/corpus/templates/brain/server/db/index.ts +18 -0
- package/corpus/templates/brain/server/db/schema.ts +197 -1
- package/corpus/templates/brain/server/jobs/distillation-queue.ts +12 -5
- package/corpus/templates/brain/server/lib/audiences.ts +708 -0
- package/corpus/templates/brain/server/lib/brain-health.ts +251 -38
- package/corpus/templates/brain/server/lib/brain.ts +877 -50
- package/corpus/templates/brain/server/lib/capture-sanitization.ts +217 -53
- package/corpus/templates/brain/server/lib/connectors.ts +556 -70
- package/corpus/templates/brain/server/lib/demo.ts +17 -4
- package/corpus/templates/brain/server/lib/hybrid-search.ts +363 -0
- package/corpus/templates/brain/server/lib/ingest-queue.ts +104 -0
- package/corpus/templates/brain/server/lib/privacy-readiness.ts +24 -0
- package/corpus/templates/brain/server/lib/search-index-contracts.ts +72 -0
- package/corpus/templates/brain/server/lib/search-index.ts +714 -0
- package/corpus/templates/brain/server/lib/search.ts +120 -4
- package/corpus/templates/brain/server/lib/sensitivity-policy.ts +174 -0
- package/corpus/templates/brain/server/lib/slack-events.ts +281 -0
- package/corpus/templates/brain/server/onboarding.ts +29 -0
- package/corpus/templates/brain/server/plugins/db.ts +204 -0
- package/corpus/templates/brain/server/register-secrets.ts +46 -0
- package/corpus/templates/brain/server/routes/api/_agent-native/brain/ingest.post.ts +36 -16
- package/corpus/templates/brain/server/routes/api/_agent-native/brain/slack-events.post.ts +28 -0
- package/corpus/templates/brain/shared/types.ts +10 -0
- package/dist/cli/code-agent-executor.d.ts +8 -9
- package/dist/cli/code-agent-executor.d.ts.map +1 -1
- package/dist/cli/code-agent-executor.js +9 -3
- package/dist/cli/code-agent-executor.js.map +1 -1
- package/dist/cli/code-agent-runs.d.ts +5 -0
- package/dist/cli/code-agent-runs.d.ts.map +1 -1
- package/dist/cli/code-agent-runs.js +33 -2
- package/dist/cli/code-agent-runs.js.map +1 -1
- package/dist/cli/migration-codemod.d.ts.map +1 -1
- package/dist/cli/migration-codemod.js +48 -10
- package/dist/cli/migration-codemod.js.map +1 -1
- package/dist/cli/multi-frontier-runs.d.ts +131 -0
- package/dist/cli/multi-frontier-runs.d.ts.map +1 -0
- package/dist/cli/multi-frontier-runs.js +529 -0
- package/dist/cli/multi-frontier-runs.js.map +1 -0
- package/dist/coding-tools/index.d.ts +1 -0
- package/dist/coding-tools/index.d.ts.map +1 -1
- package/dist/coding-tools/index.js +25 -7
- package/dist/coding-tools/index.js.map +1 -1
- package/dist/collab/awareness.d.ts +2 -2
- package/dist/collab/awareness.d.ts.map +1 -1
- package/dist/embeddings/index.d.ts +24 -0
- package/dist/embeddings/index.d.ts.map +1 -0
- package/dist/embeddings/index.js +172 -0
- package/dist/embeddings/index.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/notifications/routes.d.ts +3 -3
- package/dist/progress/routes.d.ts +1 -1
- package/dist/resources/handlers.d.ts +1 -1
- package/dist/search/index.d.ts +88 -0
- package/dist/search/index.d.ts.map +1 -0
- package/dist/search/index.js +267 -0
- package/dist/search/index.js.map +1 -0
- package/dist/server/agent-engine-api-key-route.d.ts +1 -1
- package/package.json +3 -1
|
@@ -4,15 +4,19 @@ import { and, desc, eq, inArray, like, or } from "drizzle-orm";
|
|
|
4
4
|
import { z } from "zod";
|
|
5
5
|
|
|
6
6
|
import { getDb, schema } from "../server/db/index.js";
|
|
7
|
+
import { ensureCaptureAudience } from "../server/lib/audiences.js";
|
|
7
8
|
import {
|
|
8
9
|
contentHash,
|
|
9
10
|
getAccessibleCapture,
|
|
11
|
+
invalidateDerivedForCapture,
|
|
10
12
|
nowIso,
|
|
11
13
|
parseJson,
|
|
12
14
|
readBrainSettings,
|
|
15
|
+
recordBlockedCapture,
|
|
13
16
|
stableJson,
|
|
14
17
|
} from "../server/lib/brain.js";
|
|
15
18
|
import { sanitizeCaptureForStorage } from "../server/lib/capture-sanitization.js";
|
|
19
|
+
import { enqueueCaptureInvalidation } from "../server/lib/ingest-queue.js";
|
|
16
20
|
import { redactSensitiveText } from "../server/lib/search.js";
|
|
17
21
|
import type { BrainCaptureKind, BrainSourceProvider } from "../shared/types.js";
|
|
18
22
|
import { idSchema, stringArrayCliSchema } from "./_schemas.js";
|
|
@@ -21,6 +25,10 @@ function reviewPreview(value: string) {
|
|
|
21
25
|
return redactSensitiveText(value).replace(/\s+/g, " ").trim().slice(0, 320);
|
|
22
26
|
}
|
|
23
27
|
|
|
28
|
+
function canEditSource(role: "viewer" | "editor" | "admin" | "owner") {
|
|
29
|
+
return role === "editor" || role === "admin" || role === "owner";
|
|
30
|
+
}
|
|
31
|
+
|
|
24
32
|
async function findDerivedCitationRefs(captureId: string) {
|
|
25
33
|
const db = getDb();
|
|
26
34
|
const needle = `%${captureId}%`;
|
|
@@ -95,35 +103,51 @@ export default defineAction({
|
|
|
95
103
|
const source =
|
|
96
104
|
sourceAccess.resource as typeof schema.brainSources.$inferSelect;
|
|
97
105
|
const captures = await db
|
|
98
|
-
.select()
|
|
106
|
+
.select({ id: schema.brainRawCaptures.id })
|
|
99
107
|
.from(schema.brainRawCaptures)
|
|
100
108
|
.where(
|
|
101
109
|
args.includeNonTranscript
|
|
102
|
-
?
|
|
110
|
+
? and(
|
|
111
|
+
eq(schema.brainRawCaptures.sourceId, source.id),
|
|
112
|
+
eq(schema.brainRawCaptures.sensitivityDisposition, "allowed"),
|
|
113
|
+
)
|
|
103
114
|
: and(
|
|
104
115
|
eq(schema.brainRawCaptures.sourceId, source.id),
|
|
105
116
|
eq(schema.brainRawCaptures.kind, "transcript"),
|
|
117
|
+
eq(schema.brainRawCaptures.sensitivityDisposition, "allowed"),
|
|
106
118
|
),
|
|
107
119
|
)
|
|
108
120
|
.orderBy(desc(schema.brainRawCaptures.capturedAt))
|
|
109
121
|
.limit(args.limit);
|
|
110
|
-
for (const
|
|
122
|
+
for (const candidate of captures) {
|
|
123
|
+
const access = await getAccessibleCapture(candidate.id);
|
|
124
|
+
if (!access) continue;
|
|
125
|
+
rows.push({ capture: access.capture, source: access.source });
|
|
126
|
+
}
|
|
111
127
|
}
|
|
112
128
|
|
|
113
129
|
if (args.captureIds?.length) {
|
|
114
130
|
const captures = await db
|
|
115
|
-
.select()
|
|
131
|
+
.select({ id: schema.brainRawCaptures.id })
|
|
116
132
|
.from(schema.brainRawCaptures)
|
|
117
|
-
.where(
|
|
133
|
+
.where(
|
|
134
|
+
and(
|
|
135
|
+
inArray(schema.brainRawCaptures.id, args.captureIds),
|
|
136
|
+
eq(schema.brainRawCaptures.sensitivityDisposition, "allowed"),
|
|
137
|
+
),
|
|
138
|
+
)
|
|
118
139
|
.limit(args.captureIds.length);
|
|
119
|
-
for (const
|
|
120
|
-
|
|
140
|
+
for (const candidate of captures) {
|
|
141
|
+
const access = await getAccessibleCapture(candidate.id);
|
|
142
|
+
if (!access || !canEditSource(access.role)) continue;
|
|
143
|
+
if (
|
|
144
|
+
!args.includeNonTranscript &&
|
|
145
|
+
access.capture.kind !== "transcript"
|
|
146
|
+
) {
|
|
121
147
|
continue;
|
|
122
148
|
}
|
|
123
|
-
const access = await getAccessibleCapture(capture.id);
|
|
124
|
-
if (!access) continue;
|
|
125
149
|
rows.push({
|
|
126
|
-
capture,
|
|
150
|
+
capture: access.capture,
|
|
127
151
|
source: access.source,
|
|
128
152
|
});
|
|
129
153
|
}
|
|
@@ -139,26 +163,6 @@ export default defineAction({
|
|
|
139
163
|
const hasDerivedRefs =
|
|
140
164
|
derivedRefs.knowledgeIds.length > 0 ||
|
|
141
165
|
derivedRefs.proposalIds.length > 0;
|
|
142
|
-
if (!args.dryRun && hasDerivedRefs && !args.allowCitationDrift) {
|
|
143
|
-
results.push({
|
|
144
|
-
id: row.capture.id,
|
|
145
|
-
sourceId: row.capture.sourceId,
|
|
146
|
-
externalId: row.capture.externalId,
|
|
147
|
-
title: row.capture.title,
|
|
148
|
-
capturedAt: row.capture.capturedAt,
|
|
149
|
-
beforeLength,
|
|
150
|
-
afterLength: beforeLength,
|
|
151
|
-
method: "skipped-derived-citations",
|
|
152
|
-
rawContentRetained: true,
|
|
153
|
-
skipped: true,
|
|
154
|
-
skipReason:
|
|
155
|
-
"Capture already has derived knowledge/proposals citing its current text. Run with dryRun first, then re-distill or pass allowCitationDrift=true intentionally.",
|
|
156
|
-
dependentKnowledgeIds: derivedRefs.knowledgeIds,
|
|
157
|
-
dependentProposalIds: derivedRefs.proposalIds,
|
|
158
|
-
preview: reviewPreview(row.capture.content),
|
|
159
|
-
});
|
|
160
|
-
continue;
|
|
161
|
-
}
|
|
162
166
|
const sanitized = await sanitizeCaptureForStorage({
|
|
163
167
|
kind: row.capture.kind as BrainCaptureKind,
|
|
164
168
|
title: row.capture.title,
|
|
@@ -183,17 +187,139 @@ export default defineAction({
|
|
|
183
187
|
const sanitizer = sanitized.metadata.captureSanitization as
|
|
184
188
|
| Record<string, unknown>
|
|
185
189
|
| undefined;
|
|
190
|
+
if (hasDerivedRefs && !args.allowCitationDrift) {
|
|
191
|
+
results.push({
|
|
192
|
+
id: row.capture.id,
|
|
193
|
+
sourceId: row.capture.sourceId,
|
|
194
|
+
externalId: row.capture.externalId,
|
|
195
|
+
title: sanitized.title,
|
|
196
|
+
capturedAt: row.capture.capturedAt,
|
|
197
|
+
beforeLength,
|
|
198
|
+
afterLength: sanitized.content.length,
|
|
199
|
+
method: sanitizer?.method ?? "not-sanitized",
|
|
200
|
+
rawContentRetained: sanitizer?.rawContentRetained ?? false,
|
|
201
|
+
skipped: true,
|
|
202
|
+
skipReason: "cited-derived-data",
|
|
203
|
+
dependentKnowledgeIds: derivedRefs.knowledgeIds,
|
|
204
|
+
dependentProposalIds: derivedRefs.proposalIds,
|
|
205
|
+
preview: reviewPreview(sanitized.content),
|
|
206
|
+
});
|
|
207
|
+
continue;
|
|
208
|
+
}
|
|
209
|
+
if (
|
|
210
|
+
!args.dryRun &&
|
|
211
|
+
sanitized.decision &&
|
|
212
|
+
sanitized.decision.disposition !== "allowed"
|
|
213
|
+
) {
|
|
214
|
+
await recordBlockedCapture({
|
|
215
|
+
id: row.capture.id,
|
|
216
|
+
existing: row.capture,
|
|
217
|
+
source: row.source,
|
|
218
|
+
values: {
|
|
219
|
+
id: row.capture.id,
|
|
220
|
+
sourceId: row.capture.sourceId,
|
|
221
|
+
externalId: row.capture.externalId,
|
|
222
|
+
title: row.capture.title,
|
|
223
|
+
kind: row.capture.kind as BrainCaptureKind,
|
|
224
|
+
content: row.capture.content,
|
|
225
|
+
metadata: parseJson<Record<string, unknown>>(
|
|
226
|
+
row.capture.metadataJson,
|
|
227
|
+
{},
|
|
228
|
+
),
|
|
229
|
+
capturedAt: row.capture.capturedAt,
|
|
230
|
+
status: row.capture.status,
|
|
231
|
+
},
|
|
232
|
+
decision: sanitized.decision,
|
|
233
|
+
retentionHours: settings.quarantineRetentionHours ?? 72,
|
|
234
|
+
});
|
|
235
|
+
results.push({
|
|
236
|
+
id: row.capture.id,
|
|
237
|
+
sourceId: row.capture.sourceId,
|
|
238
|
+
externalId: row.capture.externalId,
|
|
239
|
+
title: "Privacy-blocked capture",
|
|
240
|
+
capturedAt: row.capture.capturedAt,
|
|
241
|
+
beforeLength,
|
|
242
|
+
afterLength: 0,
|
|
243
|
+
method: sanitizer?.method ?? "deterministic",
|
|
244
|
+
rawContentRetained: false,
|
|
245
|
+
skipped: false,
|
|
246
|
+
dependentKnowledgeIds: derivedRefs.knowledgeIds,
|
|
247
|
+
dependentProposalIds: derivedRefs.proposalIds,
|
|
248
|
+
preview: "",
|
|
249
|
+
});
|
|
250
|
+
continue;
|
|
251
|
+
}
|
|
186
252
|
if (!args.dryRun) {
|
|
253
|
+
const nextContentHash = await contentHash(sanitized.content);
|
|
254
|
+
let aclHash = row.capture.audienceAclHash;
|
|
255
|
+
if (!aclHash && sanitized.decision?.disposition === "allowed") {
|
|
256
|
+
aclHash = (
|
|
257
|
+
await ensureCaptureAudience({
|
|
258
|
+
captureId: row.capture.id,
|
|
259
|
+
source: row.source,
|
|
260
|
+
memberEmails:
|
|
261
|
+
row.source.visibility === "org"
|
|
262
|
+
? undefined
|
|
263
|
+
: [row.source.ownerEmail],
|
|
264
|
+
})
|
|
265
|
+
).aclHash;
|
|
266
|
+
}
|
|
187
267
|
await db
|
|
188
268
|
.update(schema.brainRawCaptures)
|
|
189
269
|
.set({
|
|
190
270
|
title: sanitized.title,
|
|
191
|
-
content:
|
|
192
|
-
|
|
271
|
+
content:
|
|
272
|
+
sanitized.decision?.disposition === "allowed"
|
|
273
|
+
? sanitized.content
|
|
274
|
+
: "",
|
|
275
|
+
contentHash:
|
|
276
|
+
sanitized.decision?.disposition === "allowed"
|
|
277
|
+
? nextContentHash
|
|
278
|
+
: await contentHash(""),
|
|
193
279
|
metadataJson: stableJson(sanitized.metadata),
|
|
280
|
+
sensitivityDisposition:
|
|
281
|
+
sanitized.decision?.disposition === "allowed"
|
|
282
|
+
? "allowed"
|
|
283
|
+
: "pending",
|
|
284
|
+
sensitivityPolicyVersion: sanitized.decision?.policyVersion ?? null,
|
|
285
|
+
audienceAclHash:
|
|
286
|
+
sanitized.decision?.disposition === "allowed" ? aclHash : null,
|
|
287
|
+
status:
|
|
288
|
+
sanitized.decision?.disposition === "allowed"
|
|
289
|
+
? row.capture.status
|
|
290
|
+
: "ignored",
|
|
194
291
|
updatedAt: nowIso(),
|
|
195
292
|
})
|
|
196
293
|
.where(eq(schema.brainRawCaptures.id, row.capture.id));
|
|
294
|
+
if (
|
|
295
|
+
hasDerivedRefs &&
|
|
296
|
+
(sanitized.decision?.disposition !== "allowed" ||
|
|
297
|
+
row.capture.contentHash !== nextContentHash)
|
|
298
|
+
) {
|
|
299
|
+
await invalidateDerivedForCapture(row.capture.id);
|
|
300
|
+
}
|
|
301
|
+
await enqueueCaptureInvalidation({
|
|
302
|
+
captureId: row.capture.id,
|
|
303
|
+
sourceId: row.capture.sourceId,
|
|
304
|
+
reason:
|
|
305
|
+
sanitized.decision?.disposition === "allowed"
|
|
306
|
+
? "content-changed"
|
|
307
|
+
: "sensitivity-changed",
|
|
308
|
+
previous: {
|
|
309
|
+
contentHash: row.capture.contentHash ?? undefined,
|
|
310
|
+
sensitivityPolicyVersion:
|
|
311
|
+
row.capture.sensitivityPolicyVersion ?? undefined,
|
|
312
|
+
aclHash: row.capture.audienceAclHash ?? undefined,
|
|
313
|
+
},
|
|
314
|
+
next:
|
|
315
|
+
sanitized.decision?.disposition === "allowed" && aclHash
|
|
316
|
+
? {
|
|
317
|
+
contentHash: nextContentHash,
|
|
318
|
+
sensitivityPolicyVersion: sanitized.decision.policyVersion,
|
|
319
|
+
aclHash,
|
|
320
|
+
}
|
|
321
|
+
: undefined,
|
|
322
|
+
});
|
|
197
323
|
}
|
|
198
324
|
results.push({
|
|
199
325
|
id: row.capture.id,
|
|
@@ -204,7 +330,7 @@ export default defineAction({
|
|
|
204
330
|
beforeLength,
|
|
205
331
|
afterLength: sanitized.content.length,
|
|
206
332
|
method: sanitizer?.method ?? "not-sanitized",
|
|
207
|
-
rawContentRetained: sanitizer?.rawContentRetained ??
|
|
333
|
+
rawContentRetained: sanitizer?.rawContentRetained ?? false,
|
|
208
334
|
skipped: false,
|
|
209
335
|
dependentKnowledgeIds: derivedRefs.knowledgeIds,
|
|
210
336
|
dependentProposalIds: derivedRefs.proposalIds,
|
|
@@ -6,6 +6,7 @@ import { z } from "zod";
|
|
|
6
6
|
|
|
7
7
|
import { getDb, schema } from "../server/db/index.js";
|
|
8
8
|
import {
|
|
9
|
+
assertDerivedAudienceAccess,
|
|
9
10
|
nowIso,
|
|
10
11
|
parseJson,
|
|
11
12
|
serializeProposal,
|
|
@@ -31,6 +32,7 @@ export default defineAction({
|
|
|
31
32
|
run: async ({ id, decision, reviewerNotes, publishCanonical }) => {
|
|
32
33
|
const access = await assertAccess("brain-proposal", id, "editor");
|
|
33
34
|
const proposal = access.resource;
|
|
35
|
+
await assertDerivedAudienceAccess(proposal);
|
|
34
36
|
if (proposal.status !== "pending") {
|
|
35
37
|
throw new Error(`Proposal ${id} is already ${proposal.status}`);
|
|
36
38
|
}
|
|
@@ -53,6 +53,15 @@ export default defineAction({
|
|
|
53
53
|
.enum(["manual", "generic", "clips", "slack", "granola", "github"])
|
|
54
54
|
.optional()
|
|
55
55
|
.describe("Restrict results to one Brain source provider."),
|
|
56
|
+
kind: z
|
|
57
|
+
.enum(["transcript", "note", "message", "document", "generic"])
|
|
58
|
+
.optional()
|
|
59
|
+
.describe("Restrict capture results to one capture kind."),
|
|
60
|
+
projectId: z
|
|
61
|
+
.string()
|
|
62
|
+
.min(1)
|
|
63
|
+
.optional()
|
|
64
|
+
.describe("Restrict captures to a Brain project."),
|
|
56
65
|
status: z.string().optional().describe("Restrict results to one status."),
|
|
57
66
|
limit: z.coerce.number().int().min(1).max(100).default(25),
|
|
58
67
|
}),
|
|
@@ -61,7 +70,7 @@ export default defineAction({
|
|
|
61
70
|
publicAgent: {
|
|
62
71
|
expose: true,
|
|
63
72
|
readOnly: true,
|
|
64
|
-
requiresAuth:
|
|
73
|
+
requiresAuth: true,
|
|
65
74
|
isConsequential: false,
|
|
66
75
|
},
|
|
67
76
|
run: async (args) => {
|
|
@@ -2,7 +2,30 @@ import { defineAction } from "@agent-native/core";
|
|
|
2
2
|
import { z } from "zod";
|
|
3
3
|
|
|
4
4
|
import { writeBrainSettings } from "../server/lib/brain.js";
|
|
5
|
-
import { publishTierSchema } from "./_schemas.js";
|
|
5
|
+
import { publishTierSchema, stringArrayCliSchema } from "./_schemas.js";
|
|
6
|
+
|
|
7
|
+
function hasPrivacyWeakeningInstruction(value: string) {
|
|
8
|
+
const matches = value.matchAll(
|
|
9
|
+
/\b(?:allow|permit|release|retain|store|index|ignore|override|relax|weaken|lower|disable)\b/gi,
|
|
10
|
+
);
|
|
11
|
+
for (const match of matches) {
|
|
12
|
+
const index = match.index ?? 0;
|
|
13
|
+
const prefix = value.slice(Math.max(0, index - 32), index);
|
|
14
|
+
if (/\b(?:do not|don't|never|must not|cannot|can't)\s*$/i.test(prefix)) {
|
|
15
|
+
continue;
|
|
16
|
+
}
|
|
17
|
+
return true;
|
|
18
|
+
}
|
|
19
|
+
return false;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const privacyInstructionsSchema = z
|
|
23
|
+
.string()
|
|
24
|
+
.max(4_000)
|
|
25
|
+
.refine((value) => !hasPrivacyWeakeningInstruction(value), {
|
|
26
|
+
message:
|
|
27
|
+
"Privacy instructions may only add stricter exclusions; they cannot permit, retain, store, index, or release content.",
|
|
28
|
+
});
|
|
6
29
|
|
|
7
30
|
export default defineAction({
|
|
8
31
|
description: "Update Brain template settings.",
|
|
@@ -20,6 +43,18 @@ export default defineAction({
|
|
|
20
43
|
captureSanitizationEnabled: z.coerce.boolean().optional(),
|
|
21
44
|
captureSanitizationModel: z.string().max(160).optional(),
|
|
22
45
|
captureSanitizationInstructions: z.string().max(4000).optional(),
|
|
46
|
+
privacyClassifierModel: z.string().max(160).optional(),
|
|
47
|
+
privacyClassifierEngine: z.string().max(160).optional(),
|
|
48
|
+
sensitivityCustomInstructions: privacyInstructionsSchema.optional(),
|
|
49
|
+
publicChannelExclusionPatterns: stringArrayCliSchema({ max: 100 })
|
|
50
|
+
.transform((patterns) => patterns.map((pattern) => pattern.trim()))
|
|
51
|
+
.optional(),
|
|
52
|
+
quarantineRetentionHours: z.coerce
|
|
53
|
+
.number()
|
|
54
|
+
.int()
|
|
55
|
+
.min(1)
|
|
56
|
+
.max(720)
|
|
57
|
+
.optional(),
|
|
23
58
|
connectorPollMinutes: z.coerce.number().int().min(5).max(1440).optional(),
|
|
24
59
|
requireCitations: z.coerce.boolean().optional(),
|
|
25
60
|
autoArchiveResolved: z.coerce.boolean().optional(),
|
|
@@ -5,6 +5,7 @@ import { z } from "zod";
|
|
|
5
5
|
|
|
6
6
|
import { getDb, schema } from "../server/db/index.js";
|
|
7
7
|
import {
|
|
8
|
+
assertDerivedAudienceAccess,
|
|
8
9
|
nowIso,
|
|
9
10
|
parseJson,
|
|
10
11
|
serializeProposal,
|
|
@@ -23,6 +24,7 @@ export default defineAction({
|
|
|
23
24
|
run: async ({ proposalId, title, body, rationale }) => {
|
|
24
25
|
const access = await assertAccess("brain-proposal", proposalId, "editor");
|
|
25
26
|
const proposal = access.resource;
|
|
27
|
+
await assertDerivedAudienceAccess(proposal);
|
|
26
28
|
if (proposal.status !== "pending") {
|
|
27
29
|
throw new Error(`Proposal ${proposalId} is already ${proposal.status}`);
|
|
28
30
|
}
|
|
@@ -12,6 +12,7 @@ interface DistillationRequest {
|
|
|
12
12
|
captureId?: string;
|
|
13
13
|
queueId?: string;
|
|
14
14
|
sourceId?: string;
|
|
15
|
+
claimToken?: string;
|
|
15
16
|
requestedAt?: string;
|
|
16
17
|
instructions?: string | null;
|
|
17
18
|
guidance?: Record<string, unknown>;
|
|
@@ -52,15 +53,17 @@ async function clearRequest(captureId: string): Promise<void> {
|
|
|
52
53
|
async function claimDistillation(
|
|
53
54
|
captureId: string,
|
|
54
55
|
queueId?: string,
|
|
55
|
-
): Promise<
|
|
56
|
+
): Promise<string | null> {
|
|
56
57
|
try {
|
|
57
58
|
const payload = await callAction(
|
|
58
59
|
"claim-distillation" as any,
|
|
59
60
|
{ captureId, queueId } as any,
|
|
60
61
|
);
|
|
61
|
-
return
|
|
62
|
+
return payload?.claimed && typeof payload.claimToken === "string"
|
|
63
|
+
? payload.claimToken
|
|
64
|
+
: null;
|
|
62
65
|
} catch {
|
|
63
|
-
return
|
|
66
|
+
return null;
|
|
64
67
|
}
|
|
65
68
|
}
|
|
66
69
|
|
|
@@ -121,18 +124,19 @@ export function useDistillationBridge(): void {
|
|
|
121
124
|
request.requestedAt ?? "0"
|
|
122
125
|
}`;
|
|
123
126
|
if (dispatched.current.has(dispatchKey)) continue;
|
|
124
|
-
const
|
|
125
|
-
if (!
|
|
127
|
+
const claimToken = await claimDistillation(capture.id, queueId);
|
|
128
|
+
if (!claimToken || !queueId) continue;
|
|
126
129
|
dispatched.current.add(dispatchKey);
|
|
127
130
|
|
|
128
131
|
sendToAgentChat({
|
|
129
|
-
message:
|
|
132
|
+
message: buildMessage(capture, queueId, claimToken),
|
|
130
133
|
context: JSON.stringify(
|
|
131
134
|
{
|
|
132
|
-
request,
|
|
135
|
+
request: { ...request, claimToken },
|
|
133
136
|
capture: summarizeCapture(capture),
|
|
134
137
|
instructions: request.instructions ?? undefined,
|
|
135
138
|
guidance: request.guidance,
|
|
139
|
+
claim: { queueId, claimToken },
|
|
136
140
|
},
|
|
137
141
|
null,
|
|
138
142
|
2,
|
|
@@ -164,12 +168,17 @@ export function useDistillationBridge(): void {
|
|
|
164
168
|
}, [capturesKey, dispatchableCaptures, capturesQuery.dataUpdatedAt]);
|
|
165
169
|
}
|
|
166
170
|
|
|
167
|
-
function buildMessage(
|
|
171
|
+
function buildMessage(
|
|
172
|
+
capture: BrainCaptureReviewItem,
|
|
173
|
+
queueId: string,
|
|
174
|
+
claimToken: string,
|
|
175
|
+
) {
|
|
168
176
|
return (
|
|
169
177
|
`Distill Brain capture ${capture.id} (${capture.title}). ` +
|
|
170
178
|
`Use get-capture with includeRawContent=true before exact quote ` +
|
|
171
179
|
`validation, write durable company knowledge with write-knowledge, then ` +
|
|
172
|
-
`mark the capture distilled or ignored
|
|
180
|
+
`mark the capture distilled or ignored with queueId ${queueId} and ` +
|
|
181
|
+
`claimToken ${claimToken}.`
|
|
173
182
|
);
|
|
174
183
|
}
|
|
175
184
|
|
|
@@ -13,6 +13,7 @@ export interface NavigationState {
|
|
|
13
13
|
view: BrainView;
|
|
14
14
|
path?: string;
|
|
15
15
|
query?: string;
|
|
16
|
+
projectId?: string;
|
|
16
17
|
source?: string;
|
|
17
18
|
sourceType?: string;
|
|
18
19
|
type?: string;
|
|
@@ -45,6 +46,7 @@ export function useNavigationState() {
|
|
|
45
46
|
view: viewFromPath(localPathname),
|
|
46
47
|
path: appPath(`${localPathname}${search}`),
|
|
47
48
|
query: params.get("q") || undefined,
|
|
49
|
+
projectId: params.get("projectId") || undefined,
|
|
48
50
|
source: params.get("source") || undefined,
|
|
49
51
|
sourceType: params.get("type") || undefined,
|
|
50
52
|
type: params.get("type") || undefined,
|
|
@@ -64,6 +66,7 @@ export function useNavigationState() {
|
|
|
64
66
|
getCommandPath: (navCommand) => {
|
|
65
67
|
const params = new URLSearchParams();
|
|
66
68
|
if (navCommand.query) params.set("q", navCommand.query);
|
|
69
|
+
if (navCommand.projectId) params.set("projectId", navCommand.projectId);
|
|
67
70
|
if (navCommand.source) params.set("source", navCommand.source);
|
|
68
71
|
if (navCommand.type || navCommand.sourceType) {
|
|
69
72
|
params.set("type", navCommand.type ?? navCommand.sourceType ?? "");
|