@sigrun/sigrun 0.4.2 → 0.5.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/CHANGELOG.md +37 -1
- package/README.md +46 -23
- package/dist/commands/expand.js +37 -5
- package/dist/curation/attribution.js +135 -0
- package/dist/curation/deletion.js +32 -0
- package/dist/curation/gate.js +50 -0
- package/dist/curation/host.js +342 -0
- package/dist/curation/internal.js +54 -0
- package/dist/curation/names.js +5 -0
- package/dist/curation/overlays.js +325 -0
- package/dist/curation/paths.js +17 -0
- package/dist/curation/run-doc.js +64 -0
- package/dist/curation/runs.js +579 -0
- package/dist/curation/scan.js +30 -0
- package/dist/curation/skill-tool.js +308 -0
- package/dist/curation/store.js +444 -0
- package/dist/curation/targets.js +77 -0
- package/dist/curation/usage.js +124 -0
- package/dist/curation/wrap.js +212 -0
- package/dist/extension.d.ts +282 -0
- package/dist/harness/boot.js +209 -9
- package/dist/harness/builtins.js +12 -9
- package/dist/harness/event-bus.js +64 -0
- package/dist/harness/loader.js +114 -1
- package/dist/harness/naming.js +6 -0
- package/dist/harness/packages.js +14 -9
- package/dist/harness/reserved-sections.js +2 -0
- package/dist/harness/seam-host.js +139 -26
- package/dist/harness/settings.js +2 -2
- package/dist/harness/tools.js +70 -0
- package/dist/jobs/fire.js +33 -11
- package/dist/jobs/skills-refs.js +40 -0
- package/dist/sandbox/policy.js +14 -0
- package/dist/server/blocks/answers.js +9 -3
- package/dist/server/conversation-ops.js +21 -3
- package/dist/server/helpers.js +1 -1
- package/dist/server/host-bridge.js +6 -1
- package/dist/server/notifications.js +6 -0
- package/dist/server/routes/meta.js +8 -3
- package/dist/server/routes/submissions.js +5 -0
- package/dist/server/routes/surfaces.js +3 -1
- package/dist/server/routes/users.js +7 -0
- package/dist/server/schema-fixture.js +168 -0
- package/dist/server/session-meta.js +12 -10
- package/dist/settings/core-sections.js +9 -0
- package/dist/settings/registry.js +6 -0
- package/dist/skills/index.js +88 -19
- package/node_modules/@sigrun/shared/CHANGELOG.md +3 -1
- package/node_modules/@sigrun/shared/dist/access.d.ts +3 -0
- package/node_modules/@sigrun/shared/dist/access.js +8 -0
- package/node_modules/@sigrun/shared/dist/session-meta.d.ts +15 -4
- package/node_modules/@sigrun/shared/dist/settings.d.ts +7 -5
- package/node_modules/@sigrun/shared/package.json +1 -1
- package/package.json +9 -7
- package/webui/dist/assets/{index-DWmsAQl2.js → index-BtczbUXo.js} +1 -1
- package/webui/dist/index.html +1 -1
|
@@ -20,6 +20,7 @@ import { AgentDoc, configure } from "@earendil-works/pi-durable";
|
|
|
20
20
|
import { accessOf, isSharing, } from "@sigrun/shared/access";
|
|
21
21
|
import { extname } from "node:path";
|
|
22
22
|
import { touchActivity } from "../harness/activity.js";
|
|
23
|
+
import { ensureRequestId, recordAuthor, } from "../curation/attribution.js";
|
|
23
24
|
import { resolveServedModelRefreshing } from "../harness/providers/refresh.js";
|
|
24
25
|
import { isSuperuser } from "./auth/principal.js";
|
|
25
26
|
import { isConfinedPath, parseSubmissionInput } from "./content-parts.js";
|
|
@@ -247,6 +248,9 @@ export async function submitOp(ctx, conversation, input, opts = {}) {
|
|
|
247
248
|
if (!parsed.ok) {
|
|
248
249
|
throw new ConversationOpError({ error: "invalid_input" });
|
|
249
250
|
}
|
|
251
|
+
// Every input carries a requestId (curator P1): the caller's own id, or
|
|
252
|
+
// a generated `sigrun:` UUID — the pre-admission attribution key.
|
|
253
|
+
const requestId = ensureRequestId(opts.requestId);
|
|
250
254
|
// Spec §2/§3 binding contract: a conversation pinned to an unserved
|
|
251
255
|
// model fails explicitly AT SUBMIT — 4xx with the available ids —
|
|
252
256
|
// never a silent fallback, and never a settled no_model run surfaced
|
|
@@ -316,7 +320,11 @@ export async function submitOp(ctx, conversation, input, opts = {}) {
|
|
|
316
320
|
else {
|
|
317
321
|
try {
|
|
318
322
|
const env = await envThunk();
|
|
319
|
-
|
|
323
|
+
// Curator A3: the learned root resolves through the conversation
|
|
324
|
+
// OWNER (SessionMetaDoc), not the submitting user — a shared-write
|
|
325
|
+
// submitter expands against the owner's learned skills.
|
|
326
|
+
const meta = await opened.harness.snapshot(SessionMetaDoc, conversation.id, BACKGROUND_CONTEXT);
|
|
327
|
+
expansion = await expandInput(parsed.parts, opened.commandExpansion, env, BACKGROUND_CONTEXT, meta?.owner);
|
|
320
328
|
}
|
|
321
329
|
catch (error) {
|
|
322
330
|
if (error instanceof CommandUsageError) {
|
|
@@ -356,13 +364,20 @@ export async function submitOp(ctx, conversation, input, opts = {}) {
|
|
|
356
364
|
const draft = {
|
|
357
365
|
type: "input",
|
|
358
366
|
content,
|
|
359
|
-
|
|
367
|
+
requestId,
|
|
360
368
|
...(whenBusy === undefined ? {} : { whenBusy: whenBusy }),
|
|
361
369
|
...(entry === undefined ? {} : { entry }),
|
|
362
370
|
};
|
|
371
|
+
// Attribution (curator P1), two writes per input: `byRequest` BEFORE the
|
|
372
|
+
// admission (the requestId exists pre-admission, so this never races; an
|
|
373
|
+
// orphan record from a failed submit is harmless) and `bySubmission`
|
|
374
|
+
// right after it returns — the key the live run's input ids resolve
|
|
375
|
+
// against. An `undefined` user records nothing (unattributed).
|
|
376
|
+
await recordAuthor(conversation, { requestId }, opts.user, BACKGROUND_CONTEXT);
|
|
363
377
|
// The submit pass-through is retired (§6.7): the memory seam's
|
|
364
378
|
// runSettled owns capture, so submissions admit directly.
|
|
365
379
|
const submission = await conversation.submit(draft, BACKGROUND_CONTEXT);
|
|
380
|
+
await recordAuthor(conversation, { submissionId: submission.id }, opts.user, BACKGROUND_CONTEXT);
|
|
366
381
|
// Admission stamps activity: the sidebar's activity sort must move the
|
|
367
382
|
// moment a prompt lands — before the run settles.
|
|
368
383
|
await touchActivity(opened.harness, conversation.id, opened.now, BACKGROUND_CONTEXT);
|
|
@@ -394,7 +409,10 @@ export async function listConversationsOp(ctx, caller, filters = {}) {
|
|
|
394
409
|
// flags, and the create source (spec §9.5).
|
|
395
410
|
const meta = await opened.harness.snapshot(SessionMetaDoc, record.id, BACKGROUND_CONTEXT);
|
|
396
411
|
const access = accessOf(caller, meta ?? {});
|
|
397
|
-
// Hidden is absent from the list (spec §5.9)
|
|
412
|
+
// Hidden is absent from the list (spec §5.9) — and internal means
|
|
413
|
+
// hidden for EVERY caller (curator P5, accessOf rule 0): the
|
|
414
|
+
// superuser and the bridge's trusted read are covered by the same
|
|
415
|
+
// line, so no caller-specific skip can drift.
|
|
398
416
|
if (access === "none")
|
|
399
417
|
continue;
|
|
400
418
|
// The archived filter (absent = all).
|
package/dist/server/helpers.js
CHANGED
|
@@ -86,7 +86,7 @@ function autoEnableRemovals(settings, env) {
|
|
|
86
86
|
const server = entry;
|
|
87
87
|
if (server.enabled === false || server.disabled === true)
|
|
88
88
|
continue;
|
|
89
|
-
// @sigrun/ext-mcp's server default is "codemode"; its
|
|
89
|
+
// @sigrun/ext-mcp's server default is "codemode"; its deprecated
|
|
90
90
|
// alias `codemode-deferred` normalizes to "codemode".
|
|
91
91
|
const exposure = server.exposure === "codemode-deferred"
|
|
92
92
|
? "codemode"
|
|
@@ -59,11 +59,13 @@ export function createHostBridge(ctx) {
|
|
|
59
59
|
const conversation = await conversationOf(id);
|
|
60
60
|
// `user` is accepted and NOT verified: extensions own their
|
|
61
61
|
// user mapping (spec §9.5) — a channel's unmapped user submits
|
|
62
|
-
// fine, and owner attribution happened at create.
|
|
62
|
+
// fine, and owner attribution happened at create. P1 records
|
|
63
|
+
// it verbatim as the input's author.
|
|
63
64
|
return await submitOp(ctx, conversation, input, {
|
|
64
65
|
...(opts?.requestId === undefined
|
|
65
66
|
? {}
|
|
66
67
|
: { requestId: opts.requestId }),
|
|
68
|
+
...(opts?.user === undefined ? {} : { user: opts.user }),
|
|
67
69
|
...(opts?.whenBusy === undefined
|
|
68
70
|
? {}
|
|
69
71
|
: { whenBusy: opts.whenBusy }),
|
|
@@ -284,5 +286,8 @@ export function createHostBridge(ctx) {
|
|
|
284
286
|
ctx.surfaces.bumpRev(slug, id, key, update);
|
|
285
287
|
},
|
|
286
288
|
},
|
|
289
|
+
// Curator P6 (A9): the boot-built curation host, verbatim — the
|
|
290
|
+
// ONE instance every seam host reaches through this bridge.
|
|
291
|
+
curation: ctx.opened.curationHost,
|
|
287
292
|
};
|
|
288
293
|
}
|
|
@@ -132,6 +132,12 @@ export function startNotifications(ctx) {
|
|
|
132
132
|
if (mode === "never")
|
|
133
133
|
return;
|
|
134
134
|
const meta = await metaOf(conversationId);
|
|
135
|
+
// Curator P5: an internal run never notifies (no "Reply ready" for
|
|
136
|
+
// curator work). The bus already withholds the host event; this is
|
|
137
|
+
// the belt-and-braces read-side gate (a hand-published or future
|
|
138
|
+
// emit path cannot leak the run).
|
|
139
|
+
if (meta?.internal === true)
|
|
140
|
+
return;
|
|
135
141
|
if (mode === "cron" && meta?.source !== "cron")
|
|
136
142
|
return;
|
|
137
143
|
if (mode === "unwatched" &&
|
|
@@ -178,8 +178,8 @@ export function registerMetaRoutes(app, ctx) {
|
|
|
178
178
|
// templates appear the moment their files land, no reload command. The
|
|
179
179
|
// core + extension halves remain boot constants; see `commands.ts` for
|
|
180
180
|
// the scope rulings.
|
|
181
|
-
app.get("/v1/commands", async () => {
|
|
182
|
-
// The workspace root (the first of the
|
|
181
|
+
app.get("/v1/commands", async (request) => {
|
|
182
|
+
// The workspace root (the first of the four) reads through a
|
|
183
183
|
// workspace env, exactly like skill discovery at generation time;
|
|
184
184
|
// the catalog scan uses the root conversation's env — the static
|
|
185
185
|
// workspace every conversation resolves to today.
|
|
@@ -188,7 +188,12 @@ export function registerMetaRoutes(app, ctx) {
|
|
|
188
188
|
if (env === undefined) {
|
|
189
189
|
throw new Error("envBuilder returned no ExecutionEnv");
|
|
190
190
|
}
|
|
191
|
-
|
|
191
|
+
// Curator A3: learned skills are the CALLER's — a user's `/skill:`
|
|
192
|
+
// menu shows their own learned skills and nobody else's (an
|
|
193
|
+
// unidentified caller sees none).
|
|
194
|
+
const skills = await listSkills(env, BACKGROUND_CONTEXT, expansion.skills, {
|
|
195
|
+
owner: request.principal.user,
|
|
196
|
+
});
|
|
192
197
|
const templates = expandableTemplates(await discoverTemplates(env, BACKGROUND_CONTEXT, expansion.templates), expansion.commandNames());
|
|
193
198
|
return {
|
|
194
199
|
commands: [
|
|
@@ -46,6 +46,11 @@ export function registerSubmissionRoutes(app, ctx) {
|
|
|
46
46
|
...(body.requestId === undefined
|
|
47
47
|
? {}
|
|
48
48
|
: { requestId: String(body.requestId) }),
|
|
49
|
+
// Attribution (curator P1): the resolved principal's user is
|
|
50
|
+
// the input's author; an unidentified caller records nothing.
|
|
51
|
+
...(request.principal.user === undefined
|
|
52
|
+
? {}
|
|
53
|
+
: { user: request.principal.user }),
|
|
49
54
|
...(body.whenBusy === undefined ? {} : { whenBusy: body.whenBusy }),
|
|
50
55
|
...(request.query?.waitMs ===
|
|
51
56
|
undefined
|
|
@@ -508,7 +508,9 @@ export function registerSurfaceRoutes(app, ctx) {
|
|
|
508
508
|
if (chatActionStartsTurn(block.tree, body.action)) {
|
|
509
509
|
const answerPayload = (body.payload ?? null);
|
|
510
510
|
const wrapped = { ok: true, rev: block.state.rev };
|
|
511
|
-
await answerBlock(ctx, id, entryId, body.action, answerPayload, actionId
|
|
511
|
+
await answerBlock(ctx, id, entryId, body.action, answerPayload, actionId,
|
|
512
|
+
// The acting user authors the answer turn (curator P1).
|
|
513
|
+
request.principal.user);
|
|
512
514
|
if (actionId !== undefined) {
|
|
513
515
|
ctx.surfaces.chatDedupRecord(actionId, address, fingerprint, wrapped);
|
|
514
516
|
}
|
|
@@ -16,6 +16,7 @@ import { withClaimLock } from "../auth/claim-lock.js";
|
|
|
16
16
|
import { effectiveAdmin, storedGroups } from "../../harness/identity/groups.js";
|
|
17
17
|
import { transferOwnership } from "../ownership.js";
|
|
18
18
|
import { validateGrantPath } from "../../sandbox/grants.js";
|
|
19
|
+
import { removeUserCuration } from "../../curation/deletion.js";
|
|
19
20
|
/** The wire form of one directory user. `admin` stays the STORED flag;
|
|
20
21
|
* `groupAdmin` is DERIVED from stored groups and retained for one release
|
|
21
22
|
* so the users UI keeps rendering. */
|
|
@@ -265,6 +266,12 @@ export function registerUserRoutes(app, ctx) {
|
|
|
265
266
|
// §10.6 step 5: data/state stay for the recipient; core owns only
|
|
266
267
|
// the departing user's credentials, settings, and every cache slug.
|
|
267
268
|
await ctx.opened.extensionStores.removeUserData(user);
|
|
269
|
+
// Curator A10 (P2): the departing user's learned skills and curation
|
|
270
|
+
// state are REMOVED, never transferred — a skill distilled from
|
|
271
|
+
// their conversations can carry their paths and preferences, so it
|
|
272
|
+
// must not load for the recipient. In-flight curator runs fail
|
|
273
|
+
// closed through the stores' user-exists gate (nothing recreated).
|
|
274
|
+
await removeUserCuration(ctx.managedPaths.userAreas(user));
|
|
268
275
|
return await reply.code(204).send();
|
|
269
276
|
});
|
|
270
277
|
}
|
|
@@ -336,6 +336,174 @@ export const VIEW_SCHEMA = {
|
|
|
336
336
|
}
|
|
337
337
|
}
|
|
338
338
|
},
|
|
339
|
+
{
|
|
340
|
+
"kind": "object",
|
|
341
|
+
"fields": {
|
|
342
|
+
"name": {
|
|
343
|
+
"kind": "string"
|
|
344
|
+
},
|
|
345
|
+
"description": {
|
|
346
|
+
"kind": "string"
|
|
347
|
+
},
|
|
348
|
+
"parameters": {
|
|
349
|
+
"kind": "object",
|
|
350
|
+
"fields": {
|
|
351
|
+
"type": {
|
|
352
|
+
"kind": "string"
|
|
353
|
+
},
|
|
354
|
+
"required": {
|
|
355
|
+
"kind": "array",
|
|
356
|
+
"variants": [
|
|
357
|
+
{
|
|
358
|
+
"kind": "string"
|
|
359
|
+
}
|
|
360
|
+
]
|
|
361
|
+
},
|
|
362
|
+
"properties": {
|
|
363
|
+
"kind": "object",
|
|
364
|
+
"fields": {
|
|
365
|
+
"action": {
|
|
366
|
+
"kind": "object",
|
|
367
|
+
"fields": {
|
|
368
|
+
"anyOf": {
|
|
369
|
+
"kind": "array",
|
|
370
|
+
"variants": [
|
|
371
|
+
{
|
|
372
|
+
"kind": "object",
|
|
373
|
+
"fields": {
|
|
374
|
+
"type": {
|
|
375
|
+
"kind": "string"
|
|
376
|
+
},
|
|
377
|
+
"const": {
|
|
378
|
+
"kind": "string"
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
]
|
|
383
|
+
},
|
|
384
|
+
"description": {
|
|
385
|
+
"kind": "string"
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
},
|
|
389
|
+
"name": {
|
|
390
|
+
"kind": "object",
|
|
391
|
+
"fields": {
|
|
392
|
+
"type": {
|
|
393
|
+
"kind": "string"
|
|
394
|
+
},
|
|
395
|
+
"description": {
|
|
396
|
+
"kind": "string"
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
},
|
|
400
|
+
"file_path": {
|
|
401
|
+
"kind": "object",
|
|
402
|
+
"fields": {
|
|
403
|
+
"type": {
|
|
404
|
+
"kind": "string"
|
|
405
|
+
},
|
|
406
|
+
"description": {
|
|
407
|
+
"kind": "string"
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
},
|
|
411
|
+
"content": {
|
|
412
|
+
"kind": "object",
|
|
413
|
+
"fields": {
|
|
414
|
+
"type": {
|
|
415
|
+
"kind": "string"
|
|
416
|
+
},
|
|
417
|
+
"description": {
|
|
418
|
+
"kind": "string"
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
},
|
|
422
|
+
"old_text": {
|
|
423
|
+
"kind": "object",
|
|
424
|
+
"fields": {
|
|
425
|
+
"type": {
|
|
426
|
+
"kind": "string"
|
|
427
|
+
},
|
|
428
|
+
"description": {
|
|
429
|
+
"kind": "string"
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
},
|
|
433
|
+
"new_text": {
|
|
434
|
+
"kind": "object",
|
|
435
|
+
"fields": {
|
|
436
|
+
"type": {
|
|
437
|
+
"kind": "string"
|
|
438
|
+
},
|
|
439
|
+
"description": {
|
|
440
|
+
"kind": "string"
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
},
|
|
444
|
+
"absorbed_into": {
|
|
445
|
+
"kind": "object",
|
|
446
|
+
"fields": {
|
|
447
|
+
"type": {
|
|
448
|
+
"kind": "string"
|
|
449
|
+
},
|
|
450
|
+
"description": {
|
|
451
|
+
"kind": "string"
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
},
|
|
455
|
+
"reason": {
|
|
456
|
+
"kind": "object",
|
|
457
|
+
"fields": {
|
|
458
|
+
"type": {
|
|
459
|
+
"kind": "string"
|
|
460
|
+
},
|
|
461
|
+
"description": {
|
|
462
|
+
"kind": "string"
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
},
|
|
466
|
+
"overlay_root": {
|
|
467
|
+
"kind": "object",
|
|
468
|
+
"fields": {
|
|
469
|
+
"anyOf": {
|
|
470
|
+
"kind": "array",
|
|
471
|
+
"variants": [
|
|
472
|
+
{
|
|
473
|
+
"kind": "object",
|
|
474
|
+
"fields": {
|
|
475
|
+
"type": {
|
|
476
|
+
"kind": "string"
|
|
477
|
+
},
|
|
478
|
+
"const": {
|
|
479
|
+
"kind": "string"
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
]
|
|
484
|
+
},
|
|
485
|
+
"description": {
|
|
486
|
+
"kind": "string"
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
},
|
|
490
|
+
"remove": {
|
|
491
|
+
"kind": "object",
|
|
492
|
+
"fields": {
|
|
493
|
+
"type": {
|
|
494
|
+
"kind": "string"
|
|
495
|
+
},
|
|
496
|
+
"description": {
|
|
497
|
+
"kind": "string"
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
},
|
|
339
507
|
{
|
|
340
508
|
"kind": "object",
|
|
341
509
|
"fields": {
|
|
@@ -29,10 +29,10 @@
|
|
|
29
29
|
* other's fields (the mutation line serializes commits).
|
|
30
30
|
*
|
|
31
31
|
* Server-owned fields (`autoName`, `autoNameAttempts`,
|
|
32
|
-
* `lastActivityAt`, `jobId`, `deliver`) are written by host
|
|
33
|
-
* (activity.ts, the A2 auto-namer, fire.ts's job stamp
|
|
34
|
-
* patch surface — the strict
|
|
35
|
-
* `invalid_field`.
|
|
32
|
+
* `lastActivityAt`, `jobId`, `deliver`, `internal`) are written by host
|
|
33
|
+
* code only (activity.ts, the A2 auto-namer, fire.ts's job stamp, the
|
|
34
|
+
* curator's creating commit); they are NOT patch surface — the strict
|
|
35
|
+
* validator keeps rejecting them as `invalid_field`.
|
|
36
36
|
*/
|
|
37
37
|
import { defineDoc } from "@earendil-works/pi-durable";
|
|
38
38
|
import { isSharing } from "@sigrun/shared/access";
|
|
@@ -158,12 +158,14 @@ export function validateSessionMetaPatch(body) {
|
|
|
158
158
|
case "source":
|
|
159
159
|
case "owner":
|
|
160
160
|
case "jobId":
|
|
161
|
-
case "deliver":
|
|
162
|
-
|
|
163
|
-
//
|
|
164
|
-
//
|
|
165
|
-
//
|
|
166
|
-
//
|
|
161
|
+
case "deliver":
|
|
162
|
+
case "internal": {
|
|
163
|
+
// Server-only (spec §5.9 + gateway C7 + curator P5): the KEY
|
|
164
|
+
// itself is the client error, whatever the value — even a `null`
|
|
165
|
+
// clear. The job stamp fields move with the fire (fire.ts's
|
|
166
|
+
// creating commit) and the delivery targets ride the job spec,
|
|
167
|
+
// never a meta patch; the internal-run marker moves with the
|
|
168
|
+
// curator's creating commit (markInternal) alone.
|
|
167
169
|
return { error: "invalid_field", field: key };
|
|
168
170
|
}
|
|
169
171
|
case "sharing": {
|
|
@@ -504,6 +504,15 @@ export const CORE_SETTINGS_SECTIONS = [
|
|
|
504
504
|
help: "The built-in OIDC/OAuth2 login extension — applies at restart",
|
|
505
505
|
apply: applyOf("builtinExtensions.oauth"),
|
|
506
506
|
},
|
|
507
|
+
{
|
|
508
|
+
type: "toggle",
|
|
509
|
+
// Absent means ON (the built-in set defaults enabled).
|
|
510
|
+
default: true,
|
|
511
|
+
name: "builtinExtensions.curator",
|
|
512
|
+
label: "Curator",
|
|
513
|
+
help: "The built-in curator extension — applies at restart",
|
|
514
|
+
apply: applyOf("builtinExtensions.curator"),
|
|
515
|
+
},
|
|
507
516
|
],
|
|
508
517
|
},
|
|
509
518
|
],
|
|
@@ -175,6 +175,12 @@ export const CORE_SETTING_KEYS = [
|
|
|
175
175
|
section: "extensions",
|
|
176
176
|
reason: "the built-in set resolves at boot (a disabled built-in never loads)",
|
|
177
177
|
},
|
|
178
|
+
{
|
|
179
|
+
key: "builtinExtensions.curator",
|
|
180
|
+
apply: "restart",
|
|
181
|
+
section: "extensions",
|
|
182
|
+
reason: "the built-in set resolves at boot (a disabled built-in never loads)",
|
|
183
|
+
},
|
|
178
184
|
{
|
|
179
185
|
key: "autoEnableCodemode",
|
|
180
186
|
apply: "live",
|