@sigrun/sigrun 0.1.6 → 0.2.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 +104 -0
- package/README.md +352 -36
- package/dist/blocks/schema.js +9 -2
- package/dist/blocks/validate.js +57 -8
- package/dist/cli.js +26 -2
- package/dist/config.js +67 -1
- package/dist/extension.d.ts +189 -15
- package/dist/harness/ambient.js +39 -185
- package/dist/harness/blocks/entries.js +47 -0
- package/dist/harness/blocks/state.js +58 -0
- package/dist/harness/blocks/tools.js +274 -0
- package/dist/harness/boot.js +253 -20
- package/dist/harness/builtins.js +15 -11
- package/dist/harness/event-bus.js +105 -10
- package/dist/harness/identity/accounts.js +186 -0
- package/dist/harness/identity/groups.js +14 -0
- package/dist/harness/identity/index.js +13 -6
- package/dist/harness/identity/json-store.js +15 -4
- package/dist/harness/identity/setup-code-check.js +9 -0
- package/dist/harness/identity/tokens.js +28 -5
- package/dist/harness/identity/users.js +111 -12
- package/dist/harness/interactions.js +292 -136
- package/dist/harness/loader.js +271 -17
- package/dist/harness/packages.js +1 -1
- package/dist/harness/reserved-sections.js +21 -0
- package/dist/harness/seam-host.js +80 -6
- package/dist/harness/settings.js +63 -2
- package/dist/harness/stores.js +241 -4
- package/dist/harness/user-services.js +391 -0
- package/dist/harness/user-tools.js +187 -0
- package/dist/harness/workspace.js +61 -57
- package/dist/jobs/fire.js +10 -16
- package/dist/sandbox/availability.js +162 -0
- package/dist/sandbox/command-observer.js +59 -0
- package/dist/sandbox/grants.js +159 -0
- package/dist/sandbox/helper.js +29 -0
- package/dist/sandbox/isolation.js +29 -0
- package/dist/sandbox/launch.js +20 -0
- package/dist/sandbox/os-account.js +49 -0
- package/dist/sandbox/policy.js +253 -0
- package/dist/sandbox/pool.js +548 -0
- package/dist/sandbox/protocol.js +167 -0
- package/dist/sandbox/sandboxed-env.js +245 -0
- package/dist/sandbox/unavailable-env.js +49 -0
- package/dist/sandbox/worker.js +281 -0
- package/dist/serve.js +82 -7
- package/dist/server/auth/access-principal.js +16 -0
- package/dist/server/auth/pipeline.js +47 -33
- package/dist/server/blocks/answers.js +94 -0
- package/dist/server/blocks/chat-surfaces.js +393 -0
- package/dist/server/blocks/notes.js +216 -0
- package/dist/server/blocks/summaries.js +328 -0
- package/dist/server/commands.js +5 -0
- package/dist/server/content-parts-lazy.js +13 -0
- package/dist/server/conversation-handler.js +2 -5
- package/dist/server/conversation-ops.js +138 -37
- package/dist/server/helpers.js +18 -6
- package/dist/server/host-bridge.js +40 -20
- package/dist/server/images/cache.js +205 -0
- package/dist/server/images/convert.js +13 -0
- package/dist/server/images/fetch.js +302 -0
- package/dist/server/images/ranges.js +42 -0
- package/dist/server/images/rewrite.js +94 -0
- package/dist/server/images/sign.js +84 -0
- package/dist/server/images/sniff.js +62 -0
- package/dist/server/index.js +144 -4
- package/dist/server/login/flows.js +126 -0
- package/dist/server/login/http.js +31 -0
- package/dist/server/login/resolve.js +133 -0
- package/dist/server/login/setup-claim.js +55 -0
- package/dist/server/login/usernames.js +15 -0
- package/dist/server/ownership.js +13 -0
- package/dist/server/routes/admin.js +58 -0
- package/dist/server/routes/auth.js +88 -2
- package/dist/server/routes/conversations.js +3 -6
- package/dist/server/routes/events.js +28 -3
- package/dist/server/routes/extensions.js +37 -5
- package/dist/server/routes/images.js +267 -0
- package/dist/server/routes/interactions.js +57 -68
- package/dist/server/routes/jobs.js +2 -8
- package/dist/server/routes/login.js +223 -0
- package/dist/server/routes/me.js +90 -0
- package/dist/server/routes/meta.js +15 -0
- package/dist/server/routes/onboarding.js +94 -84
- package/dist/server/routes/settings-sections.js +190 -0
- package/dist/server/routes/settings.js +16 -4
- package/dist/server/routes/submissions.js +1 -1
- package/dist/server/routes/surfaces.js +175 -23
- package/dist/server/routes/users.js +116 -20
- package/dist/server/schema-fixture.js +213 -0
- package/dist/server/session-meta.js +3 -0
- package/dist/server/shutdown.js +11 -4
- package/dist/server/static.js +18 -0
- package/dist/server/surfaces/dedup.js +9 -2
- package/dist/server/surfaces/service.js +181 -13
- package/dist/server/surfaces/settings-surfaces.js +597 -0
- package/dist/server/turns/submit-turn.js +26 -0
- package/dist/server/views.js +16 -0
- package/dist/settings/core-sections.js +532 -0
- package/dist/settings/pending.js +181 -0
- package/dist/settings/registry.js +453 -0
- package/dist/skills/index.js +6 -0
- package/node_modules/@sigrun/shared/CHANGELOG.md +6 -0
- package/node_modules/@sigrun/shared/dist/access.d.ts +4 -2
- package/node_modules/@sigrun/shared/dist/access.js +11 -4
- package/node_modules/@sigrun/shared/dist/ambient.d.ts +17 -74
- package/node_modules/@sigrun/shared/dist/ambient.js +8 -6
- package/node_modules/@sigrun/shared/dist/blocks/text.js +16 -2
- package/node_modules/@sigrun/shared/dist/blocks/transport.d.ts +4 -0
- package/node_modules/@sigrun/shared/dist/blocks/types.d.ts +21 -4
- package/node_modules/@sigrun/shared/dist/interactions.d.ts +22 -48
- package/node_modules/@sigrun/shared/dist/interactions.js +9 -7
- package/node_modules/@sigrun/shared/dist/settings.d.ts +27 -2
- package/node_modules/@sigrun/shared/dist/themes.d.ts +2 -3
- package/node_modules/@sigrun/shared/dist/themes.js +2 -3
- package/node_modules/@sigrun/shared/dist/turns.d.ts +14 -0
- package/node_modules/@sigrun/shared/dist/turns.js +53 -3
- package/node_modules/@sigrun/shared/dist/views.d.ts +175 -12
- package/node_modules/@sigrun/shared/dist/views.js +16 -0
- package/node_modules/@sigrun/shared/package.json +1 -1
- package/package.json +11 -6
- package/webui/dist/assets/index-CxB969qp.js +55 -0
- package/webui/dist/assets/index-yqkL6BSS.css +1 -0
- package/webui/dist/index.html +2 -2
- package/webui/dist/assets/index-CiYInlLx.css +0 -1
- package/webui/dist/assets/index-JDfOQV9z.js +0 -52
package/dist/blocks/validate.js
CHANGED
|
@@ -142,9 +142,22 @@ function checkActionButtons(ctx, buttons, path) {
|
|
|
142
142
|
checkAction(ctx, button.action, joinPath(bPath, "action"));
|
|
143
143
|
});
|
|
144
144
|
}
|
|
145
|
-
/**
|
|
145
|
+
/**
|
|
146
|
+
* Bindings outside a card template (§4.2): relative `./` bindings are
|
|
147
|
+
* legal only inside an item template. In the AMBIENT placement, every
|
|
148
|
+
* binding (absolute or relative) outside an item template is rejected —
|
|
149
|
+
* the ambient envelope carries no data document, so a bound prop has
|
|
150
|
+
* nothing to resolve against and renders blank (the 2026-10-24 ruling's
|
|
151
|
+
* items rule, extended to every bindable prop).
|
|
152
|
+
*/
|
|
146
153
|
function checkBinding(ctx, binding, path) {
|
|
147
|
-
if (
|
|
154
|
+
if (ctx.itemTemplate)
|
|
155
|
+
return;
|
|
156
|
+
if (ctx.placement === "ambient") {
|
|
157
|
+
push(ctx, joinPath(path, "$"), `ambient surfaces carry literal content only (binding "${binding.$}" outside a collection item template)`);
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
if (binding.$.startsWith("./")) {
|
|
148
161
|
push(ctx, joinPath(path, "$"), `relative binding "${binding.$}" is only valid inside a collection item template`);
|
|
149
162
|
}
|
|
150
163
|
}
|
|
@@ -152,10 +165,12 @@ function checkBinding(ctx, binding, path) {
|
|
|
152
165
|
* Any relative `./` binding on this node (§4.2): every prop value is
|
|
153
166
|
* scanned — the binding-only props (`items`, `lines`, `nodes`, `rows`,
|
|
154
167
|
* `series[].data`, `submitLabel`, `copy.text`) bind just like the
|
|
155
|
-
* bindable literals, so a hardcoded prop list would miss them.
|
|
156
|
-
* `/` bindings stay legal everywhere
|
|
157
|
-
*
|
|
158
|
-
*
|
|
168
|
+
* bindable literals, so a hardcoded prop list would miss them. Outside
|
|
169
|
+
* ambient, absolute `/` bindings stay legal everywhere and relative
|
|
170
|
+
* ones only inside item templates; in ambient, every binding outside an
|
|
171
|
+
* item template fails (checkBinding). `skip` names props whose own walk
|
|
172
|
+
* already reported (the structural literals above carry their more
|
|
173
|
+
* precise message).
|
|
159
174
|
*/
|
|
160
175
|
function checkNodeRelativeBindings(ctx, node, path, skip) {
|
|
161
176
|
if (ctx.itemTemplate)
|
|
@@ -282,6 +297,35 @@ function checkBlock(ctx, node, path, rootDepth) {
|
|
|
282
297
|
checkSchema(ctx, validator, node, path);
|
|
283
298
|
// collection-specific rules
|
|
284
299
|
if (type === "collection") {
|
|
300
|
+
// Controller ruling (2026-10-24): bindings are OUT of ambient
|
|
301
|
+
// placement — the ambient envelope carries no data document, so a
|
|
302
|
+
// bound `items` has nothing to resolve against. Literal rows only.
|
|
303
|
+
// (Reversible: extending the envelope with a data slot remains open.)
|
|
304
|
+
if (ctx.placement === "ambient" && isBinding(node.items)) {
|
|
305
|
+
// checkBinding's ambient branch fires for this prop too; the
|
|
306
|
+
// more specific collection wording wins, so skip `items` below.
|
|
307
|
+
push(ctx, joinPath(path, "items"), "ambient collections carry literal rows (items must not be a binding)");
|
|
308
|
+
}
|
|
309
|
+
// Ambient: bindables nested inside structural props are still
|
|
310
|
+
// bound nowhere (the no-data-document ruling). `groups`/`columns`
|
|
311
|
+
// sit in the structural skip set, so the generic binding walk
|
|
312
|
+
// never reaches their `title`/`label` bindables — walk them here.
|
|
313
|
+
if (ctx.placement === "ambient") {
|
|
314
|
+
const nestedBindables = [
|
|
315
|
+
["groups", "title"],
|
|
316
|
+
["columns", "label"],
|
|
317
|
+
];
|
|
318
|
+
for (const [listProp, leafProp] of nestedBindables) {
|
|
319
|
+
const list = node[listProp];
|
|
320
|
+
if (!Array.isArray(list))
|
|
321
|
+
continue;
|
|
322
|
+
list.forEach((entry, i) => {
|
|
323
|
+
if (isRec(entry) && isBinding(entry[leafProp])) {
|
|
324
|
+
checkBinding(ctx, entry[leafProp], joinPath(joinPath(joinPath(path, listProp), String(i)), leafProp));
|
|
325
|
+
}
|
|
326
|
+
});
|
|
327
|
+
}
|
|
328
|
+
}
|
|
285
329
|
const display = node.display;
|
|
286
330
|
if (typeof display === "string" &&
|
|
287
331
|
rules.collectionDisplays &&
|
|
@@ -350,9 +394,14 @@ function checkBlock(ctx, node, path, rootDepth) {
|
|
|
350
394
|
if (!check.ok)
|
|
351
395
|
push(ctx, joinPath(path, "src"), check.message);
|
|
352
396
|
}
|
|
353
|
-
// bindable props: every relative `./` binding on the node (§4.2)
|
|
397
|
+
// bindable props: every relative `./` binding on the node (§4.2).
|
|
398
|
+
// `items` joins the skip set in ambient so the collection-specific
|
|
399
|
+
// message above stays the single error at its path.
|
|
354
400
|
const structural = STRUCTURAL_LITERALS[type] ?? [];
|
|
355
|
-
|
|
401
|
+
const skip = new Set(structural);
|
|
402
|
+
if (type === "collection" && ctx.placement === "ambient")
|
|
403
|
+
skip.add("items");
|
|
404
|
+
checkNodeRelativeBindings(ctx, node, path, skip);
|
|
356
405
|
// onActivate carries an Action (§6.1): same rules as button actions
|
|
357
406
|
if (node.onActivate !== undefined) {
|
|
358
407
|
checkAction(ctx, node.onActivate, joinPath(path, "onActivate"));
|
package/dist/cli.js
CHANGED
|
@@ -49,6 +49,12 @@ import { Writable } from "node:stream";
|
|
|
49
49
|
import { resolveStoreRoots, writeFileAtomic } from "./harness/stores.js";
|
|
50
50
|
import { openIdentityStores } from "./harness/identity/index.js";
|
|
51
51
|
import { pathToFileURL } from "node:url";
|
|
52
|
+
import { effectiveMethods, loadConfig } from "./config.js";
|
|
53
|
+
import { probeIsolation, runHelperProbe, selfTestHelper } from "./sandbox/availability.js";
|
|
54
|
+
import { SettingsStore } from "./harness/settings.js";
|
|
55
|
+
import { resolveHelper } from "./sandbox/helper.js";
|
|
56
|
+
import { selfPackageRoot } from "./harness/packages.js";
|
|
57
|
+
import { createAdmissionGuard } from "./harness/identity/users.js";
|
|
52
58
|
import { serve } from "./serve.js";
|
|
53
59
|
import { ACCENT_NAMES, PALETTE_NAMES, isAccentName, isPaletteName, } from "@sigrun/shared/themes";
|
|
54
60
|
export const USAGE_TEXT = `usage: sigrun [--user <name>]
|
|
@@ -643,7 +649,7 @@ async function runProviderCommand(parsed, env, io, fetchImpl, readSecret) {
|
|
|
643
649
|
* secret exists only at create time);
|
|
644
650
|
* - `token revoke` fails loud on an unknown id.
|
|
645
651
|
*/
|
|
646
|
-
async function runIdentityCommand(parsed, env, io) {
|
|
652
|
+
async function runIdentityCommand(parsed, env, io, isolationProbe) {
|
|
647
653
|
const stores = openIdentityStores(resolveStoreRoots(env));
|
|
648
654
|
if (parsed.command === "token") {
|
|
649
655
|
switch (parsed.sub) {
|
|
@@ -679,6 +685,24 @@ async function runIdentityCommand(parsed, env, io) {
|
|
|
679
685
|
}
|
|
680
686
|
switch (parsed.sub) {
|
|
681
687
|
case "add": {
|
|
688
|
+
const config = loadConfig(env, new SettingsStore({ dir: defaultConfigDir(env) }).read());
|
|
689
|
+
const coreRoot = selfPackageRoot();
|
|
690
|
+
const isolation = await isolationProbe({
|
|
691
|
+
platform: process.platform,
|
|
692
|
+
helper: coreRoot === undefined ? undefined : resolveHelper(env, coreRoot),
|
|
693
|
+
runProbe: runHelperProbe,
|
|
694
|
+
selfTest: selfTestHelper,
|
|
695
|
+
});
|
|
696
|
+
const methods = effectiveMethods(config.auth, "127.0.0.1");
|
|
697
|
+
stores.users.configureAdmission({
|
|
698
|
+
firstWorkspace: config.workspace.path,
|
|
699
|
+
admit: createAdmissionGuard({
|
|
700
|
+
isolation,
|
|
701
|
+
mode: config.isolation.mode,
|
|
702
|
+
methods,
|
|
703
|
+
proxySecretHeader: config.auth.proxy.secretHeader,
|
|
704
|
+
}),
|
|
705
|
+
});
|
|
682
706
|
await stores.users.add({
|
|
683
707
|
user: parsed.user,
|
|
684
708
|
admin: parsed.admin ?? false,
|
|
@@ -850,7 +874,7 @@ export async function cliMain(argv, env = process.env, deps, io = {
|
|
|
850
874
|
case "token":
|
|
851
875
|
case "user": {
|
|
852
876
|
// The identity commands (Task 7): stores directly, server or not.
|
|
853
|
-
return await runIdentityCommand(parsed, env, io);
|
|
877
|
+
return await runIdentityCommand(parsed, env, io, realDeps.probeIsolation ?? probeIsolation);
|
|
854
878
|
}
|
|
855
879
|
case "provider": {
|
|
856
880
|
// The provider commands (release 0.1 Task 2b): API clients over
|
package/dist/config.js
CHANGED
|
@@ -148,6 +148,7 @@ export const DEFAULT_PROVIDER_REFRESH_MINUTES = 15;
|
|
|
148
148
|
* defaulted — they exist only when env provides them.
|
|
149
149
|
*/
|
|
150
150
|
export const DEFAULT_AUTH = {
|
|
151
|
+
sessionDays: 30,
|
|
151
152
|
methods: undefined,
|
|
152
153
|
proxy: {
|
|
153
154
|
trustedProxies: [],
|
|
@@ -323,6 +324,40 @@ function parseDev(env) {
|
|
|
323
324
|
return false;
|
|
324
325
|
throw new Error(`SIGRUN_DEV must be "true" or "false" (or 1/0), got ${JSON.stringify(raw)}`);
|
|
325
326
|
}
|
|
327
|
+
/** Parse `SIGRUN_ISOLATION`; unset/empty lets the file/default tier apply. */
|
|
328
|
+
function parseIsolationMode(env) {
|
|
329
|
+
const raw = env.SIGRUN_ISOLATION;
|
|
330
|
+
if (raw === undefined || raw === "")
|
|
331
|
+
return undefined;
|
|
332
|
+
const mode = raw.trim().toLowerCase();
|
|
333
|
+
if (mode === "auto" || mode === "always" || mode === "off")
|
|
334
|
+
return mode;
|
|
335
|
+
throw new Error(`SIGRUN_ISOLATION must be "auto", "always", or "off", got ${JSON.stringify(raw)}`);
|
|
336
|
+
}
|
|
337
|
+
function parseIsolation(env, server) {
|
|
338
|
+
return {
|
|
339
|
+
mode: parseIsolationMode(env) ?? server?.mode ?? "auto",
|
|
340
|
+
osUsers: server?.osUsers ?? false,
|
|
341
|
+
workerIdleMinutes: server?.workerIdleMinutes ?? 10,
|
|
342
|
+
};
|
|
343
|
+
}
|
|
344
|
+
/**
|
|
345
|
+
* Parse `SIGRUN_SUPERVISED` (UI-blocks U4 Task 2, spec §13.3): the
|
|
346
|
+
* `parseDev` grammar exactly ("true"/"false"/1/0, strict otherwise —
|
|
347
|
+
* the env contract is the deploy's promise that a restart policy exists,
|
|
348
|
+
* so a fuzzy parse would lie about the failure mode).
|
|
349
|
+
*/
|
|
350
|
+
function parseSupervised(env) {
|
|
351
|
+
const raw = env.SIGRUN_SUPERVISED;
|
|
352
|
+
if (raw === undefined || raw === "")
|
|
353
|
+
return undefined;
|
|
354
|
+
const lowered = raw.trim().toLowerCase();
|
|
355
|
+
if (lowered === "true" || lowered === "1")
|
|
356
|
+
return true;
|
|
357
|
+
if (lowered === "false" || lowered === "0")
|
|
358
|
+
return false;
|
|
359
|
+
throw new Error(`SIGRUN_SUPERVISED must be "true" or "false" (or 1/0), got ${JSON.stringify(raw)}`);
|
|
360
|
+
}
|
|
326
361
|
/**
|
|
327
362
|
* Parse `SIGRUN_AUTH`: a csv of `token`/`proxy` (order preserved,
|
|
328
363
|
* duplicates dropped), `none` ALONE as the empty list (authentication
|
|
@@ -398,6 +433,7 @@ function parseAuth(env, server) {
|
|
|
398
433
|
const token = env.SIGRUN_API_TOKEN?.trim() || undefined;
|
|
399
434
|
const secret = env.SIGRUN_PROXY_SECRET?.trim() || undefined;
|
|
400
435
|
return {
|
|
436
|
+
sessionDays: server?.sessionDays ?? DEFAULT_AUTH.sessionDays,
|
|
401
437
|
methods,
|
|
402
438
|
proxy: {
|
|
403
439
|
trustedProxies,
|
|
@@ -730,11 +766,15 @@ export function resolvedServerSection(config) {
|
|
|
730
766
|
proxySection !== undefined ||
|
|
731
767
|
config.auth.adminGroups.length > 0 ||
|
|
732
768
|
config.auth.allowedHosts !== undefined ||
|
|
733
|
-
config.auth.allowedOrigins !== undefined
|
|
769
|
+
config.auth.allowedOrigins !== undefined ||
|
|
770
|
+
config.auth.sessionDays !== DEFAULT_AUTH.sessionDays
|
|
734
771
|
? {
|
|
735
772
|
...(config.auth.methods === undefined
|
|
736
773
|
? {}
|
|
737
774
|
: { methods: [...config.auth.methods] }),
|
|
775
|
+
...(config.auth.sessionDays === DEFAULT_AUTH.sessionDays
|
|
776
|
+
? {}
|
|
777
|
+
: { sessionDays: config.auth.sessionDays }),
|
|
738
778
|
...(proxySection === undefined ? {} : { proxy: proxySection }),
|
|
739
779
|
...(config.auth.adminGroups.length > 0
|
|
740
780
|
? { adminGroups: [...config.auth.adminGroups] }
|
|
@@ -765,6 +805,7 @@ export function resolvedServerSection(config) {
|
|
|
765
805
|
...(webui === undefined ? {} : { webui }),
|
|
766
806
|
compaction: { ...config.compaction },
|
|
767
807
|
memorySeam: { ...config.memorySeam },
|
|
808
|
+
isolation: { ...config.isolation },
|
|
768
809
|
providers: { ...config.providers },
|
|
769
810
|
...(auth === undefined ? {} : { auth }),
|
|
770
811
|
...(metrics === undefined ? {} : { metrics }),
|
|
@@ -803,6 +844,7 @@ export function resolvedServerSection(config) {
|
|
|
803
844
|
* | `SIGRUN_IDLE_THRESHOLD` | `memorySeam.idleThresholdMs` |
|
|
804
845
|
* | `SIGRUN_WORKER_DEADLINE` | `memorySeam.workerDeadlineMs` |
|
|
805
846
|
* | `SIGRUN_QUEUE_MAX` | `memorySeam.queueMax` |
|
|
847
|
+
* | `SIGRUN_ISOLATION` | `isolation.mode` |
|
|
806
848
|
* | `SIGRUN_PROVIDER_REFRESH_MINUTES` | `providers.refreshIntervalMinutes` |
|
|
807
849
|
* | `SIGRUN_BACKUP_*` | `backup` |
|
|
808
850
|
* | `SIGRUN_MAX_UPLOAD_BYTES` | `maxUploadBytes` |
|
|
@@ -859,6 +901,9 @@ export function envServerSection(env) {
|
|
|
859
901
|
});
|
|
860
902
|
if (memorySeam !== undefined)
|
|
861
903
|
section.memorySeam = memorySeam;
|
|
904
|
+
const isolationMode = parseIsolationMode(env);
|
|
905
|
+
if (isolationMode !== undefined)
|
|
906
|
+
section.isolation = { mode: isolationMode };
|
|
862
907
|
const refreshIntervalMinutes = parseNonNegativeIntEnv(env, PROVIDER_REFRESH_MINUTES_ENV);
|
|
863
908
|
if (refreshIntervalMinutes !== undefined) {
|
|
864
909
|
section.providers = { refreshIntervalMinutes };
|
|
@@ -877,6 +922,12 @@ export function envServerSection(env) {
|
|
|
877
922
|
const dev = parseDev(env);
|
|
878
923
|
if (dev !== undefined)
|
|
879
924
|
section.dev = dev;
|
|
925
|
+
// The supervised flag (§13.3) — the same bootstrap rule as dev:
|
|
926
|
+
// projected only when the env var is SET (a defaulted flag is never
|
|
927
|
+
// persisted; the deploy's restart posture is env intent, not state).
|
|
928
|
+
const supervised = parseSupervised(env);
|
|
929
|
+
if (supervised !== undefined)
|
|
930
|
+
section.supervised = supervised;
|
|
880
931
|
return section;
|
|
881
932
|
}
|
|
882
933
|
/**
|
|
@@ -950,8 +1001,18 @@ export function effectiveServerSection(env, fileServer, config) {
|
|
|
950
1001
|
"memorySeam",
|
|
951
1002
|
"queueMax",
|
|
952
1003
|
]);
|
|
1004
|
+
emit("auth.sessionDays", config.auth.sessionDays, ["auth", "sessionDays"]);
|
|
1005
|
+
emit("isolation.mode", config.isolation.mode, ["isolation", "mode"]);
|
|
1006
|
+
emit("isolation.osUsers", config.isolation.osUsers, ["isolation", "osUsers"]);
|
|
1007
|
+
emit("isolation.workerIdleMinutes", config.isolation.workerIdleMinutes, [
|
|
1008
|
+
"isolation",
|
|
1009
|
+
"workerIdleMinutes",
|
|
1010
|
+
]);
|
|
953
1011
|
emit("providers.refreshIntervalMinutes", config.providers.refreshIntervalMinutes, ["providers", "refreshIntervalMinutes"]);
|
|
954
1012
|
emit("dev", config.dev, ["dev"]);
|
|
1013
|
+
// U4 §13.3: the supervised posture rides the effective block too —
|
|
1014
|
+
// the settings surface's restart affordance reads one place.
|
|
1015
|
+
emit("supervised", config.supervised, ["supervised"]);
|
|
955
1016
|
if (config.backup?.target === "path") {
|
|
956
1017
|
emit("backup.path", config.backup.path, ["backup", "path"]);
|
|
957
1018
|
emit("backup.staleAfterHours", config.backupStaleAfterHours, ["backup", "staleAfterHours"]);
|
|
@@ -1093,6 +1154,7 @@ export function loadConfig(env, settingsFile) {
|
|
|
1093
1154
|
backupStaleAfterHours: server?.backup?.staleAfterHours ?? DEFAULT_BACKUP_STALE_AFTER_HOURS,
|
|
1094
1155
|
compaction: parseCompaction(env, server?.compaction),
|
|
1095
1156
|
memorySeam: parseMemorySeam(env, server?.memorySeam),
|
|
1157
|
+
isolation: parseIsolation(env, server?.isolation),
|
|
1096
1158
|
providers: {
|
|
1097
1159
|
refreshIntervalMinutes: parseRefreshIntervalMinutes(env, server?.providers?.refreshIntervalMinutes),
|
|
1098
1160
|
},
|
|
@@ -1101,6 +1163,10 @@ export function loadConfig(env, settingsFile) {
|
|
|
1101
1163
|
// The development flag (§14.2/§14.4): strict env parse over the
|
|
1102
1164
|
// file's `server.dev`, default false.
|
|
1103
1165
|
dev: parseDev(env) ?? server?.dev ?? false,
|
|
1166
|
+
// The supervised posture (§13.3): env over file over false. Only
|
|
1167
|
+
// the chart and compose set the env; a file-set flag serves a
|
|
1168
|
+
// hand-rolled systemd/supervisord deploy (same restart promise).
|
|
1169
|
+
supervised: parseSupervised(env) ?? server?.supervised ?? false,
|
|
1104
1170
|
// The jobs default timezone (§8.2): env over file over the process
|
|
1105
1171
|
// TZ, validated loud (a bad name would otherwise crash the scheduler
|
|
1106
1172
|
// at evaluation time instead of failing boot).
|
package/dist/extension.d.ts
CHANGED
|
@@ -35,12 +35,12 @@
|
|
|
35
35
|
import type { AssistantMessage, Provider } from "@earendil-works/pi-ai";
|
|
36
36
|
import type { ConversationDocToken, ConversationId, Cursor, EntryId, EntryQuery, EntryRecord, Extension, InputSubmissionDraft, JsonObject, ModelRef, Page, SubmissionId } from "@earendil-works/pi-durable";
|
|
37
37
|
import type { Access, Sharing } from "@sigrun/shared/access";
|
|
38
|
-
import type {
|
|
38
|
+
import type { AmbientPlace, AmbientRecord } from "@sigrun/shared/ambient";
|
|
39
39
|
import type { Action, DataOps, IconName, Tree } from "@sigrun/shared/blocks";
|
|
40
40
|
import type { InteractionAnswer, InteractionRecord } from "@sigrun/shared/interactions";
|
|
41
41
|
import type { JobSpec } from "@sigrun/shared/jobs";
|
|
42
|
-
import type { ConversationSummary, ConversationViewWire, HealthStatus, ModelEntry, SessionMeta, SlashCommand, SubmissionRecordWire, ThinkingLevelWire, UserWire as DirectoryUserWire } from "@sigrun/shared/views";
|
|
43
|
-
export type { Access, AmbientRecord, DirectoryUserWire, InteractionRecord, JobSpec, ModelEntry, Sharing, SlashCommand, };
|
|
42
|
+
import type { AccountLinkWire, ConversationSummary, ConversationViewWire, HealthStatus, ModelEntry, SessionMeta, SlashCommand, SubmissionRecordWire, ThinkingLevelWire, UserWire as DirectoryUserWire } from "@sigrun/shared/views";
|
|
43
|
+
export type { Access, AccountLinkWire, AmbientRecord, DirectoryUserWire, InteractionRecord, JobSpec, ModelEntry, Sharing, SlashCommand, };
|
|
44
44
|
type JsonParams = JsonObject;
|
|
45
45
|
type JsonParamValue = JsonParams[string];
|
|
46
46
|
/**
|
|
@@ -63,6 +63,8 @@ export interface HandlerContext {
|
|
|
63
63
|
*/
|
|
64
64
|
export type RunSettledEvent = {
|
|
65
65
|
readonly conversationId: ConversationId;
|
|
66
|
+
/** The conversation owner at delivery; absent when the conversation is ownerless. */
|
|
67
|
+
readonly owner?: string;
|
|
66
68
|
readonly inputs: readonly SubmissionId[];
|
|
67
69
|
/** Text blocks of each input's entry (`pi.user`, or an expanded input's
|
|
68
70
|
* `sigrun.user.expanded` — the ADMITTED text), concatenated;
|
|
@@ -100,19 +102,24 @@ export type MemoryEventApi = {
|
|
|
100
102
|
export type ExtensionLifecyclePayload = {
|
|
101
103
|
conversationCreated: {
|
|
102
104
|
readonly conversationId: ConversationId;
|
|
105
|
+
readonly owner?: string;
|
|
103
106
|
};
|
|
104
107
|
conversationForked: {
|
|
105
108
|
readonly childId: ConversationId;
|
|
106
109
|
readonly parentId: ConversationId;
|
|
107
110
|
readonly at: EntryId;
|
|
111
|
+
/** The CHILD's owner at delivery; absent when the child is ownerless. */
|
|
112
|
+
readonly owner?: string;
|
|
108
113
|
};
|
|
109
114
|
conversationArchived: {
|
|
110
115
|
readonly conversationId: ConversationId;
|
|
111
116
|
readonly archived: boolean;
|
|
117
|
+
readonly owner?: string;
|
|
112
118
|
};
|
|
113
119
|
conversationIdle: {
|
|
114
120
|
readonly conversationId: ConversationId;
|
|
115
121
|
readonly idleMs: number;
|
|
122
|
+
readonly owner?: string;
|
|
116
123
|
};
|
|
117
124
|
/** Gateway C6: one terminal interaction transition — the payload mirrors
|
|
118
125
|
* the record's terminal envelope fields. */
|
|
@@ -121,6 +128,11 @@ export type ExtensionLifecyclePayload = {
|
|
|
121
128
|
readonly callId: string;
|
|
122
129
|
readonly kind: string;
|
|
123
130
|
readonly status: "answered" | "skipped" | "expired" | "cancelled";
|
|
131
|
+
/** The answering user (C5 attribution), when a human answered. */
|
|
132
|
+
readonly answeredByUser?: string;
|
|
133
|
+
/** The STORED (masked-by-construction) answer values — render-safe. */
|
|
134
|
+
readonly answer?: Readonly<Record<string, unknown>>;
|
|
135
|
+
readonly owner?: string;
|
|
124
136
|
};
|
|
125
137
|
};
|
|
126
138
|
/** A factory-result `runSettled` handler: called once per settled run, deadline-bounded. */
|
|
@@ -151,6 +163,17 @@ export type SigrunLifecycleHandlers = {
|
|
|
151
163
|
readonly harnessShutdown?: ExtensionHarnessShutdownHandler;
|
|
152
164
|
/** The user-removal transfer event; direct fan-out, not commit-derived. */
|
|
153
165
|
readonly userDeleted?: ExtensionUserDeletedHandler;
|
|
166
|
+
/**
|
|
167
|
+
* UI-blocks U4 (spec §13.2): called after a settings write COMMITS —
|
|
168
|
+
* the keys are the flattened paths the write touched (core keys in
|
|
169
|
+
* the §13.1 dotted vocabulary, this extension's own top-level keys
|
|
170
|
+
* for a `settings/<slug>.json` write). Fired for BOTH scopes: a
|
|
171
|
+
* server-scope write notifies every loaded extension; an extension's
|
|
172
|
+
* own write notifies it (and every other extension — settings are
|
|
173
|
+
* shared operator surface). Errors are caught and logged; the write
|
|
174
|
+
* already succeeded and stays successful.
|
|
175
|
+
*/
|
|
176
|
+
onSettingsChanged?(keys: readonly string[], user?: string): void | Promise<void>;
|
|
154
177
|
};
|
|
155
178
|
/**
|
|
156
179
|
* The lifecycle event names a worker's `wakeOn` may declare.
|
|
@@ -182,6 +205,31 @@ export interface ExtensionWorker {
|
|
|
182
205
|
/** The work; rejects (or overruns the worker deadline) to fail the run. */
|
|
183
206
|
run(context: HandlerContext, wake?: WorkerWake): Promise<void>;
|
|
184
207
|
}
|
|
208
|
+
/** The command and extra environment/read rights for one per-user process. */
|
|
209
|
+
export interface ServiceSpec {
|
|
210
|
+
readonly command: string;
|
|
211
|
+
readonly args: string[];
|
|
212
|
+
readonly env?: Record<string, string>;
|
|
213
|
+
readonly cwd?: string;
|
|
214
|
+
readonly readPaths?: string[];
|
|
215
|
+
}
|
|
216
|
+
/** One factory-declared long-lived per-user service. */
|
|
217
|
+
export interface UserService {
|
|
218
|
+
readonly name: string;
|
|
219
|
+
start(user: string, ctx: HandlerContext): Promise<ServiceSpec | undefined>;
|
|
220
|
+
readonly idleMinutes?: number;
|
|
221
|
+
}
|
|
222
|
+
/** A shared-stdio attachment to a running per-user service. */
|
|
223
|
+
export interface ServiceHandle {
|
|
224
|
+
readonly stdin: WritableStream<Uint8Array>;
|
|
225
|
+
readonly stdout: ReadableStream<Uint8Array>;
|
|
226
|
+
readonly stderr: ReadableStream<Uint8Array>;
|
|
227
|
+
readonly exited: Promise<{
|
|
228
|
+
code: number | null;
|
|
229
|
+
signal: string | null;
|
|
230
|
+
}>;
|
|
231
|
+
release(): void;
|
|
232
|
+
}
|
|
185
233
|
/** Three-state per-provider readiness: not configured, degraded, or ready. */
|
|
186
234
|
export type ProviderReadinessStatus = "not_configured" | "degraded" | "ready";
|
|
187
235
|
/** One readiness report: the status plus optional operator-facing detail. */
|
|
@@ -207,6 +255,42 @@ export interface ProviderContribution {
|
|
|
207
255
|
/** The per-provider readiness tracker (`/v1/auth/check` reports it verbatim). */
|
|
208
256
|
readonly readiness?: ReadinessTracker;
|
|
209
257
|
}
|
|
258
|
+
/** Core-owned state for one external login, with provider-private scratch data. */
|
|
259
|
+
export type LoginFlow = {
|
|
260
|
+
state: string;
|
|
261
|
+
callbackUrl: string;
|
|
262
|
+
data: Record<string, string>;
|
|
263
|
+
};
|
|
264
|
+
/** The identity proof a login provider returns after its callback. */
|
|
265
|
+
export type LoginResult = {
|
|
266
|
+
subject: string;
|
|
267
|
+
profile: {
|
|
268
|
+
username?: string;
|
|
269
|
+
name?: string;
|
|
270
|
+
email?: string;
|
|
271
|
+
emailVerified?: boolean;
|
|
272
|
+
};
|
|
273
|
+
groups?: string[];
|
|
274
|
+
};
|
|
275
|
+
/** One extension identity provider; core owns the flow, state and callback. */
|
|
276
|
+
export interface LoginProviderContribution {
|
|
277
|
+
readonly id: string;
|
|
278
|
+
readonly label: string;
|
|
279
|
+
readonly icon?: string;
|
|
280
|
+
/** Provider-declared provisioning policy, enforced by core. */
|
|
281
|
+
readonly policy?: {
|
|
282
|
+
readonly autoProvision?: boolean;
|
|
283
|
+
readonly allowedGroups?: string[];
|
|
284
|
+
readonly allowedDomains?: string[];
|
|
285
|
+
};
|
|
286
|
+
begin(flow: LoginFlow, ctx: HandlerContext): Promise<{
|
|
287
|
+
redirectUrl: string;
|
|
288
|
+
}>;
|
|
289
|
+
complete(flow: LoginFlow, callback: {
|
|
290
|
+
query: Record<string, string>;
|
|
291
|
+
body?: unknown;
|
|
292
|
+
}, ctx: HandlerContext): Promise<LoginResult>;
|
|
293
|
+
}
|
|
210
294
|
/** One contributed webui bundle: a selection name plus the bundle root directory. */
|
|
211
295
|
export interface WebuiBundleContribution {
|
|
212
296
|
/** The selection key (`server.webui.bundle`); must match `^[a-z][a-z0-9_-]*$`. */
|
|
@@ -231,7 +315,9 @@ export interface Principal {
|
|
|
231
315
|
readonly admin: boolean;
|
|
232
316
|
/** True for the `SIGRUN_API_TOKEN` service principal. */
|
|
233
317
|
readonly service: boolean;
|
|
234
|
-
readonly method: "none" | "token" | "proxy" | "host";
|
|
318
|
+
readonly method: "none" | "token" | "session" | "proxy" | "host";
|
|
319
|
+
/** The login provider that issued a session principal. */
|
|
320
|
+
readonly provider?: string;
|
|
235
321
|
}
|
|
236
322
|
/** One extension HTTP route, mounted at `/v1/ext/<slug>/<path>`. */
|
|
237
323
|
export interface ExtensionRouteContribution {
|
|
@@ -564,10 +650,12 @@ export type ActionResult = {
|
|
|
564
650
|
};
|
|
565
651
|
/**
|
|
566
652
|
* One declared surface (spec §7.1, verbatim shape): the placement is
|
|
567
|
-
* the generic parameter — `"view"` or `"
|
|
568
|
-
*
|
|
653
|
+
* the generic parameter — `"view"`, `"panel"`, or `"settings"` (the
|
|
654
|
+
* §13.3 settings sections adapt to settings-placement surfaces; the
|
|
655
|
+
* host builds those — extensions declare `SettingsSectionDef`s, never
|
|
656
|
+
* `SurfaceDef<"settings">` directly).
|
|
569
657
|
*/
|
|
570
|
-
export interface SurfaceDef<P extends "view" | "panel"> {
|
|
658
|
+
export interface SurfaceDef<P extends "view" | "panel" | "settings"> {
|
|
571
659
|
/** Unique within the extension. */
|
|
572
660
|
readonly id: string;
|
|
573
661
|
readonly title: string;
|
|
@@ -577,22 +665,51 @@ export interface SurfaceDef<P extends "view" | "panel"> {
|
|
|
577
665
|
readonly sharedData?: boolean;
|
|
578
666
|
/** Channels refetch data on this interval while visible (min 5000). */
|
|
579
667
|
readonly refreshMs?: number;
|
|
580
|
-
/** §7.3; default: canonical JSON of params.
|
|
581
|
-
|
|
668
|
+
/** §7.3; default: canonical JSON of params. The principal argument
|
|
669
|
+
* lets principal-owned surfaces isolate their revision/action identity. */
|
|
670
|
+
instanceKey?(params: JsonParams, principal: Principal): string;
|
|
582
671
|
render(ctx: SurfaceContext): Promise<SurfaceRenderResult>;
|
|
583
672
|
data?(ctx: SurfaceContext): Promise<SurfaceDataResult>;
|
|
584
673
|
readonly actions?: Readonly<Record<string, (ctx: SurfaceContext, payload: JsonParamValue) => Promise<ActionResult>>>;
|
|
585
674
|
}
|
|
586
675
|
/**
|
|
587
|
-
*
|
|
588
|
-
*
|
|
589
|
-
*
|
|
676
|
+
* One declared settings section (spec §13.3, verbatim shape): a static
|
|
677
|
+
* `settings`-placement form bound to the extension's settings object
|
|
678
|
+
* (`settings/<slug>.json`). The host adapts it to a surface under the
|
|
679
|
+
* extension's own `/v1/ext/<slug>/surfaces/` mount — core's own
|
|
680
|
+
* sections use the same renderer, under the reserved `core` slug.
|
|
681
|
+
*/
|
|
682
|
+
export interface SettingsSectionDef {
|
|
683
|
+
/** Unique within the extension. */
|
|
684
|
+
readonly id: string;
|
|
685
|
+
readonly title: string;
|
|
686
|
+
readonly icon?: IconName;
|
|
687
|
+
/** A root `form` block; the placement is `settings` (spec §5.7). */
|
|
688
|
+
readonly form: Tree;
|
|
689
|
+
/** Declared secrets, rendered as `secret` fields backed by the §7.7 secrets routes. */
|
|
690
|
+
readonly secrets?: readonly {
|
|
691
|
+
key: string;
|
|
692
|
+
label: string;
|
|
693
|
+
help?: string;
|
|
694
|
+
}[];
|
|
695
|
+
/** Only admins see and edit it; default true for shared sections.
|
|
696
|
+
* A user-scope section is visible to every signed-in user unless this
|
|
697
|
+
* is explicitly true. */
|
|
698
|
+
readonly admin?: boolean;
|
|
699
|
+
/** `"user"` reads/writes the calling user's settings; absent/`"server"`
|
|
700
|
+
* keeps the extension's shared settings file. */
|
|
701
|
+
readonly scope?: "server" | "user";
|
|
702
|
+
}
|
|
703
|
+
/**
|
|
704
|
+
* The `surfaces` field of a factory result (spec §7.1): declared views,
|
|
705
|
+
* panels, and settings sections. Ids are unique across ALL THREE lists
|
|
706
|
+
* (one namespace — the loader enforces it).
|
|
590
707
|
*/
|
|
591
708
|
export interface SurfacesContribution {
|
|
592
709
|
readonly views?: readonly SurfaceDef<"view">[];
|
|
593
710
|
readonly panels?: readonly SurfaceDef<"panel">[];
|
|
594
|
-
/** §13.3 settings sections
|
|
595
|
-
readonly settings?: readonly
|
|
711
|
+
/** §13.3 settings sections (UI-blocks U4 Task 3). */
|
|
712
|
+
readonly settings?: readonly SettingsSectionDef[];
|
|
596
713
|
}
|
|
597
714
|
/** One wire part of a `host.conversations.submit` input array. */
|
|
598
715
|
export type SubmissionInputPart = {
|
|
@@ -620,8 +737,38 @@ export type WhenBusyMode = NonNullable<InputSubmissionDraft["whenBusy"]>;
|
|
|
620
737
|
* factory body.
|
|
621
738
|
*/
|
|
622
739
|
export interface SigrunHost {
|
|
740
|
+
/**
|
|
741
|
+
* Publish tools for ONE user (multi-user M2). Only factory results that
|
|
742
|
+
* declared `perUserTools: true` may call this; other extensions throw
|
|
743
|
+
* `perUserTools not declared`. An empty list uninstalls the user's entry.
|
|
744
|
+
* Core wraps every tool with a committed-owner check at execute time.
|
|
745
|
+
*/
|
|
746
|
+
readonly tools: {
|
|
747
|
+
setUserTools(user: string, tools: readonly import("@earendil-works/pi-durable").ToolRegistration[]): void;
|
|
748
|
+
};
|
|
749
|
+
/** Connect to one declared per-user service; core owns its process lifecycle. */
|
|
750
|
+
readonly services: {
|
|
751
|
+
connect(name: string, user: string, ctx: HandlerContext): Promise<ServiceHandle>;
|
|
752
|
+
};
|
|
623
753
|
/** The scoped extension-stores handle (your secrets/settings sections + raw data/cache/state paths). */
|
|
624
754
|
readonly stores: {
|
|
755
|
+
/** One directory user's secrets/settings/paths, still scoped to your slug. */
|
|
756
|
+
user(user: string): {
|
|
757
|
+
readonly secrets: {
|
|
758
|
+
get(key: string): Promise<string | undefined>;
|
|
759
|
+
set(key: string, value: string): Promise<void>;
|
|
760
|
+
delete(key: string): Promise<void>;
|
|
761
|
+
};
|
|
762
|
+
readonly settings: {
|
|
763
|
+
get(): Promise<Record<string, unknown>>;
|
|
764
|
+
set(value: Record<string, unknown>): Promise<void>;
|
|
765
|
+
};
|
|
766
|
+
readonly paths: {
|
|
767
|
+
readonly data: string;
|
|
768
|
+
readonly state: string;
|
|
769
|
+
readonly cache: string;
|
|
770
|
+
};
|
|
771
|
+
};
|
|
625
772
|
/** Your section of secrets.json. */
|
|
626
773
|
readonly secrets: {
|
|
627
774
|
get(key: string): Promise<string | undefined>;
|
|
@@ -662,7 +809,7 @@ export interface SigrunHost {
|
|
|
662
809
|
readonly maxRows?: number;
|
|
663
810
|
readonly overflow?: "drop" | "wrap" | "summarize";
|
|
664
811
|
readonly refreshMs?: number;
|
|
665
|
-
readonly body?:
|
|
812
|
+
readonly body?: Tree;
|
|
666
813
|
readonly details?: JsonObject;
|
|
667
814
|
}, context: HandlerContext): Promise<void>;
|
|
668
815
|
/** Clear one ambient surface (empty replacement; keeps ownership). */
|
|
@@ -729,6 +876,8 @@ export interface SigrunHost {
|
|
|
729
876
|
configure(id: ConversationId, opts: {
|
|
730
877
|
readonly model?: string;
|
|
731
878
|
readonly thinking?: ThinkingLevelWire;
|
|
879
|
+
/** Exact extension selection; another user's `<slug>@<user>` entry is rejected. */
|
|
880
|
+
readonly extensions?: readonly string[];
|
|
732
881
|
}): Promise<void>;
|
|
733
882
|
/** Admit one manual compaction task (gateway C4, the compact route's op). */
|
|
734
883
|
compact(id: ConversationId): Promise<void>;
|
|
@@ -782,6 +931,25 @@ export interface SigrunHost {
|
|
|
782
931
|
list(): Promise<readonly DirectoryUserWire[]>;
|
|
783
932
|
get(user: string): Promise<DirectoryUserWire | undefined>;
|
|
784
933
|
};
|
|
934
|
+
/**
|
|
935
|
+
* External identities linked to directory users (multi-user §11.3): the
|
|
936
|
+
* one table for chat senders (`chat:<channel>`) and login subjects
|
|
937
|
+
* (`login:<slug>:<id>`), shared with the gateway.
|
|
938
|
+
*
|
|
939
|
+
* Core owns the file — extensions go through this surface, never
|
|
940
|
+
* `accounts.json` directly. One link per `(provider, subject)`: a
|
|
941
|
+
* re-link to the same user is a no-op returning the existing record, a
|
|
942
|
+
* link to a DIFFERENT user rejects with an `Error` whose message starts
|
|
943
|
+
* `account_link_conflict:` (the message never names the current
|
|
944
|
+
* holder — extensions match the prefix, as they do `command_not_found:`).
|
|
945
|
+
* An unknown user rejects `unknown user "<user>"`.
|
|
946
|
+
*/
|
|
947
|
+
readonly accounts: {
|
|
948
|
+
lookup(provider: string, subject: string): Promise<AccountLinkWire | undefined>;
|
|
949
|
+
link(provider: string, subject: string, user: string, via: AccountLinkWire["via"]): Promise<AccountLinkWire>;
|
|
950
|
+
unlink(provider: string, subject: string): Promise<boolean>;
|
|
951
|
+
listForUser(user: string): Promise<readonly AccountLinkWire[]>;
|
|
952
|
+
};
|
|
785
953
|
/**
|
|
786
954
|
* Resolve one principal's access to one shared resource (the whole
|
|
787
955
|
* sharing contract for extension-owned records — core stores nothing
|
|
@@ -841,12 +1009,18 @@ export interface SigrunHost {
|
|
|
841
1009
|
* begins reading each when its task lands.
|
|
842
1010
|
*/
|
|
843
1011
|
export interface SigrunExtensionResult extends SigrunLifecycleHandlers {
|
|
1012
|
+
/** Long-lived per-user sibling processes, launched and supervised by core. */
|
|
1013
|
+
readonly userServices?: readonly UserService[];
|
|
1014
|
+
/** Declare the per-user tool surface (`host.tools.setUserTools`). */
|
|
1015
|
+
readonly perUserTools?: boolean;
|
|
844
1016
|
/** The pi-durable `Extension` to install; its declared name is the slug source. */
|
|
845
1017
|
readonly extension?: Extension;
|
|
846
1018
|
/** The contributor-only identity (required when contributing without an `extension`). */
|
|
847
1019
|
readonly name?: string;
|
|
848
1020
|
/** Provider contributions, merged into the boot model catalog (last-registered-wins, collisions recorded). */
|
|
849
1021
|
readonly providers?: readonly ProviderContribution[];
|
|
1022
|
+
/** External identity providers; core qualifies ids as `login:<slug>:<id>`. */
|
|
1023
|
+
readonly loginProviders?: readonly LoginProviderContribution[];
|
|
850
1024
|
/** Webui-bundle contributions; each `dir` anchors at the extension ENTRY's directory. */
|
|
851
1025
|
readonly webuiBundles?: readonly WebuiBundleContribution[];
|
|
852
1026
|
/** The surface's health producer, aggregated into `/v1/meta` `health` (any `"degraded"` degrades). */
|