@sigrun/sigrun 0.1.6 → 0.2.1
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 +192 -0
- package/README.md +445 -42
- package/dist/blocks/build-stamp.json +13 -0
- package/dist/blocks/builders.d.ts +57 -0
- package/dist/blocks/images.d.ts +26 -0
- package/dist/blocks/index.d.ts +20 -0
- package/dist/blocks/limits.d.ts +25 -0
- package/dist/blocks/placements.d.ts +26 -0
- package/dist/blocks/schema.d.ts +38 -0
- package/dist/blocks/schema.js +9 -2
- package/dist/blocks/validate.d.ts +73 -0
- package/dist/blocks/validate.js +57 -8
- package/dist/cli.js +26 -2
- package/dist/config.js +67 -1
- package/dist/extension.d.ts +230 -22
- 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 +259 -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 +92 -6
- package/dist/harness/sections/context-files.js +1 -1
- 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 +211 -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 +31 -0
- package/dist/sandbox/os-account.js +49 -0
- package/dist/sandbox/policy.js +274 -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 +261 -0
- package/dist/serve.js +213 -25
- package/dist/server/auth/access-principal.js +16 -0
- package/dist/server/auth/pipeline.js +99 -36
- 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 +213 -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 +152 -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/notifications.js +21 -4
- 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 +274 -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 +19 -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 +11 -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 +202 -12
- package/node_modules/@sigrun/shared/dist/views.js +16 -0
- package/node_modules/@sigrun/shared/package.json +1 -1
- package/package.json +13 -9
- package/webui/dist/assets/index-SO2W4jjk.js +55 -0
- package/webui/dist/assets/index-yqkL6BSS.css +1 -0
- package/webui/dist/index.html +2 -2
- package/CONTEXT.md +0 -163
- package/webui/dist/assets/index-CiYInlLx.css +0 -1
- package/webui/dist/assets/index-JDfOQV9z.js +0 -52
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Per-resource sharing and content access (core hardening W1 Task 5, spec
|
|
3
|
-
* §5.9 ruling 6/10: per-resource sharing; groups
|
|
3
|
+
* §5.9 ruling 6/10: per-resource sharing; multi-user M3b adds groups).
|
|
4
4
|
*
|
|
5
5
|
* Part of the shared leaf package (`@sigrun/shared`): the server computes
|
|
6
6
|
* access through `accessOf`, the clients render the `sharing` state — one
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
* 2. ownerless resource → household write (everyone);
|
|
14
14
|
* 3. user === owner → write;
|
|
15
15
|
* 4. household sharing with a defined user → the shared level;
|
|
16
|
-
* 5. people sharing including the user → the shared level;
|
|
16
|
+
* 5. people sharing including the user or one of their groups → the shared level;
|
|
17
17
|
* 6. otherwise none (hidden: 404 on every :id route, absent from lists).
|
|
18
18
|
*
|
|
19
19
|
* Anonymous callers (a principal with no user) get nothing from a shared
|
|
@@ -40,8 +40,12 @@ export function accessOf(p, r) {
|
|
|
40
40
|
if (p.user !== undefined && r.sharing !== undefined) {
|
|
41
41
|
if (r.sharing.scope === "household")
|
|
42
42
|
return r.sharing.level;
|
|
43
|
-
if (r.sharing.scope === "people"
|
|
44
|
-
|
|
43
|
+
if (r.sharing.scope === "people") {
|
|
44
|
+
const sharing = r.sharing;
|
|
45
|
+
if (sharing.users.includes(p.user) ||
|
|
46
|
+
p.groups?.some((group) => sharing.groups?.includes(group)) === true) {
|
|
47
|
+
return sharing.level;
|
|
48
|
+
}
|
|
45
49
|
}
|
|
46
50
|
}
|
|
47
51
|
// 6. Hidden.
|
|
@@ -65,6 +69,9 @@ export function isSharing(value) {
|
|
|
65
69
|
case "people":
|
|
66
70
|
return (Array.isArray(record.users) &&
|
|
67
71
|
record.users.every((user) => typeof user === "string") &&
|
|
72
|
+
(record.groups === undefined ||
|
|
73
|
+
(Array.isArray(record.groups) &&
|
|
74
|
+
record.groups.every((group) => typeof group === "string"))) &&
|
|
68
75
|
(record.level === "read" || record.level === "write"));
|
|
69
76
|
default:
|
|
70
77
|
return false;
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* The ambient seam TYPES (spec: docs/approval-ux-seams.md, "Ambient
|
|
3
|
-
* surfaces"): the channel-neutral record
|
|
4
|
-
* read-mostly state — statusline slots and panel
|
|
5
|
-
* extensions through the host context and rendered
|
|
6
|
-
* generically.
|
|
3
|
+
* surfaces"; the block mapping: UI-blocks design §9.2): the channel-neutral record
|
|
4
|
+
* shapes for persistent read-mostly state — statusline slots and panel
|
|
5
|
+
* widgets — written by extensions through the host context and rendered
|
|
6
|
+
* by channels generically.
|
|
7
7
|
*
|
|
8
8
|
* The CANONICAL type declarations live here (the shared leaf package);
|
|
9
9
|
* `src/harness/ambient.ts` imports them (type-only) for the doc
|
|
@@ -17,79 +17,16 @@
|
|
|
17
17
|
* not silent composition — the pi-zentui chrome-skin lesson).
|
|
18
18
|
*
|
|
19
19
|
* Layering mirrors the interaction seam: strict envelope (key, place,
|
|
20
|
-
* budget, refresh, expires) around a body
|
|
21
|
-
* ambient
|
|
20
|
+
* budget, refresh, expires) around a block-tree body (placement
|
|
21
|
+
* `ambient`; `status`/`footer` allow one line), with opaque `details`.
|
|
22
|
+
* The legacy `line`/`rows`/`gauge`/`ledger` primitives are REJECTED
|
|
23
|
+
* (ruling 16: no compatibility layer).
|
|
22
24
|
*/
|
|
25
|
+
import type { Tree } from "./blocks/index.js";
|
|
23
26
|
import type { JsonObject } from "./json.js";
|
|
24
27
|
/** Where a channel places the surface. */
|
|
25
28
|
export type AmbientPlace = "status" | "footer" | "panel";
|
|
26
|
-
/**
|
|
27
|
-
export type AmbientRow = {
|
|
28
|
-
readonly id: string;
|
|
29
|
-
readonly label: string;
|
|
30
|
-
/** Channel-schemed glyph state (todo statuses, agent run states). */
|
|
31
|
-
readonly status?: string;
|
|
32
|
-
/** Severity ordering/color: 0 = severe .. 2 = quiet. */
|
|
33
|
-
readonly severity?: 0 | 1 | 2;
|
|
34
|
-
/** Folded away by default (warden's quiet verdicts). */
|
|
35
|
-
readonly quiet?: boolean;
|
|
36
|
-
readonly detail?: string;
|
|
37
|
-
/** Nesting depth for tree rendering (pi-goal-x task tree). */
|
|
38
|
-
readonly depth?: number;
|
|
39
|
-
/** Dependency ids for marker rendering (rpiv-todo blockedBy). */
|
|
40
|
-
readonly deps?: string[];
|
|
41
|
-
/** Present-continuous spinner label while the row is active. */
|
|
42
|
-
readonly spinnerLabel?: string;
|
|
43
|
-
readonly metrics?: AmbientMetrics;
|
|
44
|
-
};
|
|
45
|
-
/** Numeric metrics with an optional derived-format hint. */
|
|
46
|
-
export type AmbientMetrics = {
|
|
47
|
-
readonly tokens?: number;
|
|
48
|
-
readonly cost?: number;
|
|
49
|
-
readonly current?: number;
|
|
50
|
-
readonly total?: number;
|
|
51
|
-
readonly completed?: number;
|
|
52
|
-
/** Derived display hint for numeric fields ("2.5k", currency) — the
|
|
53
|
-
* host-reuse lesson from every footer extension. */
|
|
54
|
-
readonly format?: "compact" | "usd" | "percent";
|
|
55
|
-
};
|
|
56
|
-
/** The `line` primitive: one keyed statusline/footer line, optionally segmented. */
|
|
57
|
-
export type AmbientLine = {
|
|
58
|
-
readonly text: string;
|
|
59
|
-
/** Segmented composition for powerline-style slots (pi-powerline,
|
|
60
|
-
* pi-zentui): sub-segments with placement zones. */
|
|
61
|
-
readonly segments?: {
|
|
62
|
-
readonly id: string;
|
|
63
|
-
readonly text: string;
|
|
64
|
-
readonly place?: "left" | "middle" | "right";
|
|
65
|
-
}[];
|
|
66
|
-
};
|
|
67
|
-
/** The `gauge` primitive: live progress with optional check rows. */
|
|
68
|
-
export type AmbientGauge = {
|
|
69
|
-
readonly label: string;
|
|
70
|
-
readonly percent?: number;
|
|
71
|
-
readonly phase?: string;
|
|
72
|
-
readonly elapsedMs?: number;
|
|
73
|
-
readonly recentOutput?: string[];
|
|
74
|
-
/** Check rows (pi-goal-x auditor): row-shaped, same schema as rows[]. */
|
|
75
|
-
readonly checks?: AmbientRow[];
|
|
76
|
-
};
|
|
77
|
-
/** One ledger entry (primitive D): append-style feed with per-entry amend. */
|
|
78
|
-
export type AmbientLedgerEntry = {
|
|
79
|
-
readonly id: string;
|
|
80
|
-
readonly at: number;
|
|
81
|
-
readonly kind: string;
|
|
82
|
-
readonly summary: string;
|
|
83
|
-
readonly detail?: string[];
|
|
84
|
-
};
|
|
85
|
-
/** The ambient body: ONLY these keys, each from the closed primitives. */
|
|
86
|
-
export type AmbientBody = {
|
|
87
|
-
readonly line?: AmbientLine;
|
|
88
|
-
readonly rows?: AmbientRow[];
|
|
89
|
-
readonly gauge?: AmbientGauge;
|
|
90
|
-
readonly ledger?: AmbientLedgerEntry[];
|
|
91
|
-
};
|
|
92
|
-
/** The strict envelope + closed-primitive body of one ambient surface. */
|
|
29
|
+
/** The strict envelope + block-tree body of one ambient surface. */
|
|
93
30
|
export type AmbientRecord = {
|
|
94
31
|
/** The keyed slot id ("goal", "rpiv-todos", "fabric"). */
|
|
95
32
|
readonly key: string;
|
|
@@ -105,7 +42,13 @@ export type AmbientRecord = {
|
|
|
105
42
|
/** Auto-clear rule: "never" stays until replaced with an empty body. */
|
|
106
43
|
readonly expires: "never" | "idle";
|
|
107
44
|
readonly updatedAt: number;
|
|
108
|
-
|
|
45
|
+
/**
|
|
46
|
+
* The ambient body: a block tree (placement `ambient`). `status` and
|
|
47
|
+
* `footer` allow one line — a single line-level block or one
|
|
48
|
+
* `columns` of one-line blocks. Undefined = the surface is cleared.
|
|
49
|
+
* Legacy-shaped bodies fail validation (ruling 16).
|
|
50
|
+
*/
|
|
51
|
+
readonly body?: Tree;
|
|
109
52
|
/** Opaque extension-owned payload; rendered collapsed, never interpreted. */
|
|
110
53
|
readonly details?: JsonObject;
|
|
111
54
|
};
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* The ambient seam TYPES (spec: docs/approval-ux-seams.md, "Ambient
|
|
3
|
-
* surfaces"): the channel-neutral record
|
|
4
|
-
* read-mostly state — statusline slots and panel
|
|
5
|
-
* extensions through the host context and rendered
|
|
6
|
-
* generically.
|
|
3
|
+
* surfaces"; the block mapping: UI-blocks design §9.2): the channel-neutral record
|
|
4
|
+
* shapes for persistent read-mostly state — statusline slots and panel
|
|
5
|
+
* widgets — written by extensions through the host context and rendered
|
|
6
|
+
* by channels generically.
|
|
7
7
|
*
|
|
8
8
|
* The CANONICAL type declarations live here (the shared leaf package);
|
|
9
9
|
* `src/harness/ambient.ts` imports them (type-only) for the doc
|
|
@@ -17,7 +17,9 @@
|
|
|
17
17
|
* not silent composition — the pi-zentui chrome-skin lesson).
|
|
18
18
|
*
|
|
19
19
|
* Layering mirrors the interaction seam: strict envelope (key, place,
|
|
20
|
-
* budget, refresh, expires) around a body
|
|
21
|
-
* ambient
|
|
20
|
+
* budget, refresh, expires) around a block-tree body (placement
|
|
21
|
+
* `ambient`; `status`/`footer` allow one line), with opaque `details`.
|
|
22
|
+
* The legacy `line`/`rows`/`gauge`/`ledger` primitives are REJECTED
|
|
23
|
+
* (ruling 16: no compatibility layer).
|
|
22
24
|
*/
|
|
23
25
|
export {};
|
|
@@ -287,7 +287,14 @@ function renderPage(page, ctx) {
|
|
|
287
287
|
let main;
|
|
288
288
|
const collection = firstCollection(page.main);
|
|
289
289
|
if (page.empty !== undefined && collection !== undefined) {
|
|
290
|
-
|
|
290
|
+
// Literal rows (ambient's ruling shape) count as present for the
|
|
291
|
+
// `empty` gate; a binding resolves against the data model.
|
|
292
|
+
const bound = collection.items;
|
|
293
|
+
const items = Array.isArray(collection.items)
|
|
294
|
+
? collection.items
|
|
295
|
+
: bound === undefined
|
|
296
|
+
? undefined
|
|
297
|
+
: resolvePointer(ctx.data, bound.$);
|
|
291
298
|
if (!Array.isArray(items) || items.length === 0) {
|
|
292
299
|
main = [page.empty.title];
|
|
293
300
|
if (page.empty.hint !== undefined && page.empty.hint !== "")
|
|
@@ -475,7 +482,14 @@ function renderDiff(block) {
|
|
|
475
482
|
// ─── Collection ───────────────────────────────────────────────────────────
|
|
476
483
|
/** Render a `collection` in its display's text shape. */
|
|
477
484
|
function renderCollection(block, ctx) {
|
|
478
|
-
|
|
485
|
+
// Literal rows (ambient) render as-is; a binding resolves against
|
|
486
|
+
// the data model.
|
|
487
|
+
const bound = block.items;
|
|
488
|
+
const raw = Array.isArray(block.items)
|
|
489
|
+
? block.items
|
|
490
|
+
: bound === undefined
|
|
491
|
+
? undefined
|
|
492
|
+
: resolvePointer(ctx.data, bound.$);
|
|
479
493
|
const items = (Array.isArray(raw) ? raw : []).filter(isRecord);
|
|
480
494
|
const ordered = sortedItems(items, block.sort);
|
|
481
495
|
if (block.display === "table" && block.columns !== undefined && block.columns.length > 0) {
|
|
@@ -65,6 +65,10 @@ export interface SurfaceTransport {
|
|
|
65
65
|
fetchData(since?: number): Promise<SurfaceFetch>;
|
|
66
66
|
act(body: {
|
|
67
67
|
action: string;
|
|
68
|
+
/** The action's wire type (submit/edit/move/…), carried alongside
|
|
69
|
+
* the resolved name so a transport can gate on KIND (an edit
|
|
70
|
+
* named like the submit action must never read as a submit). */
|
|
71
|
+
type: string;
|
|
68
72
|
payload: JsonValue;
|
|
69
73
|
actionId: string;
|
|
70
74
|
}): Promise<SurfaceActResult>;
|
|
@@ -250,7 +250,10 @@ export interface LogLine {
|
|
|
250
250
|
}
|
|
251
251
|
export interface LogBlock extends BlockBase {
|
|
252
252
|
readonly type: "log";
|
|
253
|
-
|
|
253
|
+
/** Literal lines are legal (ambient placement requires them — the
|
|
254
|
+
* ambient envelope carries no data document); placements that
|
|
255
|
+
* resolve bindings keep the binding form. */
|
|
256
|
+
readonly lines: Bindable<readonly JsonValue[]>;
|
|
254
257
|
readonly follow?: boolean;
|
|
255
258
|
readonly wrap?: boolean;
|
|
256
259
|
/** Per-level tones; default `error`→bad, `warn`→warn, `info`→neutral, `debug`→subtle. */
|
|
@@ -373,8 +376,10 @@ export interface CollectionTime {
|
|
|
373
376
|
}
|
|
374
377
|
export interface CollectionBlock extends BlockBase {
|
|
375
378
|
readonly type: "collection";
|
|
376
|
-
/** Binding to an array
|
|
377
|
-
|
|
379
|
+
/** Binding to an array (each item has a unique `id`) — or the array
|
|
380
|
+
* itself as literal rows, the only legal shape in ambient placement
|
|
381
|
+
* (the ambient envelope carries no data document). */
|
|
382
|
+
readonly items: Binding | readonly JsonValue[];
|
|
378
383
|
/** The card template. */
|
|
379
384
|
readonly item: Card;
|
|
380
385
|
readonly display: "list" | "table" | "lanes" | "grid" | "calendar";
|
|
@@ -425,6 +430,18 @@ export interface FieldBase {
|
|
|
425
430
|
readonly maxLength?: number;
|
|
426
431
|
readonly pattern?: string;
|
|
427
432
|
}
|
|
433
|
+
export interface ToggleFieldProps {
|
|
434
|
+
/**
|
|
435
|
+
* The value an ABSENT bound value stands for (default false). Set it
|
|
436
|
+
* on toggles whose settings key defaults ON (absent = on): the
|
|
437
|
+
* channel initializes an untouched toggle to the default, and the
|
|
438
|
+
* settings surface's save skips a submitted value EQUAL to the
|
|
439
|
+
* default, so a full-form save never writes the key while the value
|
|
440
|
+
* sits at its default (explicitly flipping OFF the default still
|
|
441
|
+
* writes the key — the write is how the non-default state persists).
|
|
442
|
+
*/
|
|
443
|
+
readonly default?: boolean;
|
|
444
|
+
}
|
|
428
445
|
export interface TextField extends FieldBase {
|
|
429
446
|
readonly type: "text";
|
|
430
447
|
readonly kind?: "plain" | "search" | "email" | "url";
|
|
@@ -442,7 +459,7 @@ export interface NumberField extends FieldBase {
|
|
|
442
459
|
readonly step?: number;
|
|
443
460
|
readonly format?: Format;
|
|
444
461
|
}
|
|
445
|
-
export interface ToggleField extends FieldBase {
|
|
462
|
+
export interface ToggleField extends FieldBase, ToggleFieldProps {
|
|
446
463
|
readonly type: "toggle";
|
|
447
464
|
}
|
|
448
465
|
export interface ChoiceOption {
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The interaction seam TYPES (spec: docs/approval-ux-seams.md
|
|
3
|
-
* channel-neutral record shapes for "this conversation is
|
|
4
|
-
* human decision", written by extensions through the host
|
|
5
|
-
* answered by channels through one generic route.
|
|
2
|
+
* The interaction seam TYPES (spec: docs/approval-ux-seams.md; the block
|
|
3
|
+
* mapping: UI-blocks design §9.1): the channel-neutral record shapes for "this conversation is
|
|
4
|
+
* waiting on a human decision", written by extensions through the host
|
|
5
|
+
* context and answered by channels through one generic route.
|
|
6
6
|
*
|
|
7
7
|
* The CANONICAL type declarations live here (the shared leaf package);
|
|
8
8
|
* `src/harness/interactions.ts` imports them (type-only) for the doc
|
|
@@ -10,60 +10,29 @@
|
|
|
10
10
|
* AS-IS (envelope + body + details — never projected), so these shapes
|
|
11
11
|
* are the channel contract too (see `@sigrun/shared/views`).
|
|
12
12
|
*
|
|
13
|
-
* Layering (strict envelope /
|
|
13
|
+
* Layering (strict envelope / block-tree body / opaque details):
|
|
14
14
|
*
|
|
15
15
|
* - envelope: kind, callId, status, timestamps, answer — load-bearing for
|
|
16
16
|
* joining tool slots and lifecycle arbitration. Strictly validated.
|
|
17
|
-
* - body:
|
|
18
|
-
*
|
|
17
|
+
* - body: a block tree (placement `interaction` — a root `form` whose
|
|
18
|
+
* submit is the answer). Validated by `validateTree`; the legacy
|
|
19
|
+
* `text`/`options`/`fields`/`diff` primitives are REJECTED (ruling 16:
|
|
20
|
+
* no compatibility layer).
|
|
19
21
|
* - details: opaque extension-owned JSON. Never interpreted.
|
|
20
22
|
*/
|
|
21
|
-
import type {
|
|
23
|
+
import type { Tree } from "./blocks/index.js";
|
|
24
|
+
import type { JsonObject, JsonValue } from "./json.js";
|
|
22
25
|
/** Lifecycle of one interaction record (spec statuses). */
|
|
23
26
|
export type InteractionStatus = "pending" | "answered" | "skipped" | "expired" | "cancelled";
|
|
24
|
-
/** One choice in the `options` primitive. */
|
|
25
|
-
export type InteractionOption = {
|
|
26
|
-
readonly id: string;
|
|
27
|
-
readonly label: string;
|
|
28
|
-
/** Second line under the label (pi-advisor presets, rpiv options). */
|
|
29
|
-
readonly description?: string;
|
|
30
|
-
/** Channel-schemed color/severity hint: "danger" | "warn" | "ok" | "neutral". */
|
|
31
|
-
readonly tone?: "danger" | "warn" | "ok" | "neutral";
|
|
32
|
-
/** Pre-selected by the extension's recommendation (pi-interview conviction). */
|
|
33
|
-
readonly recommended?: boolean;
|
|
34
|
-
};
|
|
35
|
-
/** One light-form field in the `fields` primitive. */
|
|
36
|
-
export type InteractionField = {
|
|
37
|
-
readonly name: string;
|
|
38
|
-
readonly type: "string" | "number" | "boolean" | "secret";
|
|
39
|
-
readonly label?: string;
|
|
40
|
-
readonly required?: boolean;
|
|
41
|
-
};
|
|
42
|
-
/** The `diff` primitive: before/after content, optionally path-scoped. */
|
|
43
|
-
export type InteractionDiff = {
|
|
44
|
-
readonly before: string;
|
|
45
|
-
readonly after: string;
|
|
46
|
-
readonly path?: string;
|
|
47
|
-
readonly lang?: string;
|
|
48
|
-
};
|
|
49
27
|
/**
|
|
50
|
-
*
|
|
51
|
-
*
|
|
28
|
+
* What an answer carries (ruling 20): every field's value under its
|
|
29
|
+
* name — a `choice` value is its option id (or ids when `multi`), a
|
|
30
|
+
* `choice` with `allowOther` carries `{other: "…"}`.
|
|
52
31
|
*/
|
|
53
|
-
export type InteractionBody = {
|
|
54
|
-
/** Prose prompt body (markdown). */
|
|
55
|
-
readonly text?: string;
|
|
56
|
-
readonly options?: InteractionOption[];
|
|
57
|
-
readonly fields?: InteractionField[];
|
|
58
|
-
readonly diff?: InteractionDiff;
|
|
59
|
-
};
|
|
60
|
-
/** What an answer may carry: chosen ids, form values, and/or free text. */
|
|
61
32
|
export type InteractionAnswer = {
|
|
62
|
-
readonly
|
|
63
|
-
readonly values?: Readonly<Record<string, string | number | boolean>>;
|
|
64
|
-
readonly text?: string;
|
|
33
|
+
readonly values: Readonly<Record<string, JsonValue>>;
|
|
65
34
|
};
|
|
66
|
-
/** The strict envelope +
|
|
35
|
+
/** The strict envelope + block-tree body of one interaction. */
|
|
67
36
|
export type InteractionRecord = {
|
|
68
37
|
/** Extension-declared kind id (e.g. "gating.confirm", "questions.form"). */
|
|
69
38
|
readonly kind: string;
|
|
@@ -85,7 +54,12 @@ export type InteractionRecord = {
|
|
|
85
54
|
* model-asserted. Absent on the naive single-operator path (no user).
|
|
86
55
|
*/
|
|
87
56
|
readonly answeredByUser?: string;
|
|
88
|
-
|
|
57
|
+
/**
|
|
58
|
+
* The interaction body: a block tree (placement `interaction` — a
|
|
59
|
+
* root `form` whose `submit` is the answer). Legacy-shaped bodies
|
|
60
|
+
* fail validation (ruling 16).
|
|
61
|
+
*/
|
|
62
|
+
readonly body?: Tree;
|
|
89
63
|
/** Opaque extension-owned payload; rendered collapsed, never interpreted. */
|
|
90
64
|
readonly details?: JsonObject;
|
|
91
65
|
};
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The interaction seam TYPES (spec: docs/approval-ux-seams.md
|
|
3
|
-
* channel-neutral record shapes for "this conversation is
|
|
4
|
-
* human decision", written by extensions through the host
|
|
5
|
-
* answered by channels through one generic route.
|
|
2
|
+
* The interaction seam TYPES (spec: docs/approval-ux-seams.md; the block
|
|
3
|
+
* mapping: UI-blocks design §9.1): the channel-neutral record shapes for "this conversation is
|
|
4
|
+
* waiting on a human decision", written by extensions through the host
|
|
5
|
+
* context and answered by channels through one generic route.
|
|
6
6
|
*
|
|
7
7
|
* The CANONICAL type declarations live here (the shared leaf package);
|
|
8
8
|
* `src/harness/interactions.ts` imports them (type-only) for the doc
|
|
@@ -10,12 +10,14 @@
|
|
|
10
10
|
* AS-IS (envelope + body + details — never projected), so these shapes
|
|
11
11
|
* are the channel contract too (see `@sigrun/shared/views`).
|
|
12
12
|
*
|
|
13
|
-
* Layering (strict envelope /
|
|
13
|
+
* Layering (strict envelope / block-tree body / opaque details):
|
|
14
14
|
*
|
|
15
15
|
* - envelope: kind, callId, status, timestamps, answer — load-bearing for
|
|
16
16
|
* joining tool slots and lifecycle arbitration. Strictly validated.
|
|
17
|
-
* - body:
|
|
18
|
-
*
|
|
17
|
+
* - body: a block tree (placement `interaction` — a root `form` whose
|
|
18
|
+
* submit is the answer). Validated by `validateTree`; the legacy
|
|
19
|
+
* `text`/`options`/`fields`/`diff` primitives are REJECTED (ruling 16:
|
|
20
|
+
* no compatibility layer).
|
|
19
21
|
* - details: opaque extension-owned JSON. Never interpreted.
|
|
20
22
|
*/
|
|
21
23
|
export {};
|
|
@@ -149,6 +149,15 @@ export interface ServerSettings {
|
|
|
149
149
|
readonly workerDeadlineMs?: number;
|
|
150
150
|
readonly queueMax?: number;
|
|
151
151
|
};
|
|
152
|
+
/** The multi-user execution jail (M1). `mode` and `osUsers` are restart-required. */
|
|
153
|
+
readonly isolation?: {
|
|
154
|
+
/** `SIGRUN_ISOLATION` — auto, always, or off; default auto. */
|
|
155
|
+
readonly mode?: "auto" | "always" | "off";
|
|
156
|
+
/** Per-user uid/gid isolation; default false. */
|
|
157
|
+
readonly osUsers?: boolean;
|
|
158
|
+
/** Idle worker lifetime in minutes; default 10. */
|
|
159
|
+
readonly workerIdleMinutes?: number;
|
|
160
|
+
};
|
|
152
161
|
/**
|
|
153
162
|
* The provider plugins' runtime knobs (provider plugins Task 5):
|
|
154
163
|
* `SIGRUN_PROVIDER_REFRESH_MINUTES` — the interval refresh cadence
|
|
@@ -174,6 +183,8 @@ export interface ServerSettings {
|
|
|
174
183
|
readonly auth?: {
|
|
175
184
|
/** `SIGRUN_AUTH` — csv of `token`/`proxy`; absent = derive from the listen host. */
|
|
176
185
|
readonly methods?: readonly ("token" | "proxy")[];
|
|
186
|
+
/** Browser-session lifetime in days. Positive integer; default 30; LIVE. */
|
|
187
|
+
readonly sessionDays?: number;
|
|
177
188
|
readonly proxy?: {
|
|
178
189
|
readonly trustedProxies?: readonly string[];
|
|
179
190
|
readonly secretHeader?: string;
|
|
@@ -202,6 +213,16 @@ export interface ServerSettings {
|
|
|
202
213
|
* U1 blocks gallery, U3's development error bodies, and U4.
|
|
203
214
|
*/
|
|
204
215
|
readonly dev?: boolean;
|
|
216
|
+
/**
|
|
217
|
+
* The supervised flag (UI-blocks U4 Task 2, spec §13.3):
|
|
218
|
+
* `SIGRUN_SUPERVISED` (strict true/false, `1`/`0` accepted) over this
|
|
219
|
+
* key, default `false`. `true` promises a supervisor restarts the
|
|
220
|
+
* process after an intentional `exit 0` — the Helm chart and docker
|
|
221
|
+
* compose set the env (both restart the container); the Dockerfile
|
|
222
|
+
* deliberately does NOT. Gates `POST /v1/admin/restart`; `/v1/meta`
|
|
223
|
+
* reports it (`supervised`).
|
|
224
|
+
*/
|
|
225
|
+
readonly supervised?: boolean;
|
|
205
226
|
/**
|
|
206
227
|
* The backup target — exactly one of the two shapes (mirroring the
|
|
207
228
|
* env rule: `SIGRUN_BACKUP_PATH` XOR `SIGRUN_BACKUP_S3_BUCKET`), plus
|
|
@@ -268,8 +289,8 @@ export interface NotificationsSettings {
|
|
|
268
289
|
readonly runSettled?: "unwatched" | "cron" | "always" | "never";
|
|
269
290
|
}
|
|
270
291
|
/**
|
|
271
|
-
* The built-in extensions toggles
|
|
272
|
-
*
|
|
292
|
+
* The built-in extensions toggles: codemode, MCP, tool search and OAuth
|
|
293
|
+
* ship WITH core. Each key defaults to `true`; `false`
|
|
273
294
|
* removes the built-in entirely (absent from `/v1/extensions`, its config
|
|
274
295
|
* ignored with one log line). RESTART-REQUIRED: the built-in set resolves
|
|
275
296
|
* at boot, like `extensions`.
|
|
@@ -278,6 +299,10 @@ export interface BuiltinExtensionsSettings {
|
|
|
278
299
|
readonly codemode?: boolean;
|
|
279
300
|
readonly mcp?: boolean;
|
|
280
301
|
readonly toolSearch?: boolean;
|
|
302
|
+
/** Gateway (gateway C3): the built-in engine, default true. */
|
|
303
|
+
readonly gateway?: boolean;
|
|
304
|
+
/** OAuth login providers (multi-user M3b), default true. */
|
|
305
|
+
readonly oauth?: boolean;
|
|
281
306
|
}
|
|
282
307
|
/**
|
|
283
308
|
* The persisted shape: HarnessSettings pass-throughs + sigrun fields.
|
|
@@ -7,9 +7,8 @@
|
|
|
7
7
|
* consume this file from a single source; any import would drag a
|
|
8
8
|
* dependency only one client can load or resolve.
|
|
9
9
|
*
|
|
10
|
-
* Colour values are copied verbatim from the palette mockup
|
|
11
|
-
* (
|
|
12
|
-
* with the mockup ids renamed to the plan ids: palette `iron` →
|
|
10
|
+
* Colour values are copied verbatim from the UI-refresh palette mockup
|
|
11
|
+
* (palettes.html), with the mockup ids renamed to the plan ids: palette `iron` →
|
|
13
12
|
* `iron-ember`, `aurora` → `polar-night`, `birch` → `birch-moss`;
|
|
14
13
|
* accent `amber` → `baltic-amber`; token key `surface-2` → `surface2`.
|
|
15
14
|
*/
|
|
@@ -7,9 +7,8 @@
|
|
|
7
7
|
* consume this file from a single source; any import would drag a
|
|
8
8
|
* dependency only one client can load or resolve.
|
|
9
9
|
*
|
|
10
|
-
* Colour values are copied verbatim from the palette mockup
|
|
11
|
-
* (
|
|
12
|
-
* with the mockup ids renamed to the plan ids: palette `iron` →
|
|
10
|
+
* Colour values are copied verbatim from the UI-refresh palette mockup
|
|
11
|
+
* (palettes.html), with the mockup ids renamed to the plan ids: palette `iron` →
|
|
13
12
|
* `iron-ember`, `aurora` → `polar-night`, `birch` → `birch-moss`;
|
|
14
13
|
* accent `amber` → `baltic-amber`; token key `surface-2` → `surface2`.
|
|
15
14
|
*/
|
|
@@ -24,6 +24,14 @@
|
|
|
24
24
|
/** The user-entry kind an expanded `/skill:` or template input is placed
|
|
25
25
|
* as (see the module doc); grouping treats it exactly like `pi.user`. */
|
|
26
26
|
export declare const EXPANDED_USER_KIND = "sigrun.user.expanded";
|
|
27
|
+
/** The chat-blocks entry kinds (UI-blocks U5, spec §8.2/§8.3/§8.4) the
|
|
28
|
+
* grouping recognises. The server-side twins live in
|
|
29
|
+
* `src/harness/blocks/entries.ts` (this module imports nothing, so the
|
|
30
|
+
* tokens duplicate by design — one spelling per kind, pinned by the
|
|
31
|
+
* grouping tests). */
|
|
32
|
+
export declare const BLOCKS_KIND = "sigrun.blocks";
|
|
33
|
+
export declare const BLOCKS_NOTE_KIND = "sigrun.blocks.note";
|
|
34
|
+
export declare const BLOCKS_ANSWER_KIND = "sigrun.blocks.answer";
|
|
27
35
|
export interface TurnBlock {
|
|
28
36
|
readonly type: string;
|
|
29
37
|
readonly text?: string;
|
|
@@ -98,6 +106,12 @@ export type TimelineEvent = {
|
|
|
98
106
|
} | {
|
|
99
107
|
readonly kind: "text";
|
|
100
108
|
readonly text: string;
|
|
109
|
+
} | {
|
|
110
|
+
readonly kind: "blocks";
|
|
111
|
+
readonly entryId: number;
|
|
112
|
+
} | {
|
|
113
|
+
readonly kind: "note";
|
|
114
|
+
readonly entryId: number;
|
|
101
115
|
};
|
|
102
116
|
export declare function groupTurns<E extends TurnEntry>(entries: readonly E[]): Turn<E>[];
|
|
103
117
|
export declare function toolPreview(name: string, args: unknown): string;
|
|
@@ -24,6 +24,22 @@
|
|
|
24
24
|
/** The user-entry kind an expanded `/skill:` or template input is placed
|
|
25
25
|
* as (see the module doc); grouping treats it exactly like `pi.user`. */
|
|
26
26
|
export const EXPANDED_USER_KIND = "sigrun.user.expanded";
|
|
27
|
+
/** The chat-blocks entry kinds (UI-blocks U5, spec §8.2/§8.3/§8.4) the
|
|
28
|
+
* grouping recognises. The server-side twins live in
|
|
29
|
+
* `src/harness/blocks/entries.ts` (this module imports nothing, so the
|
|
30
|
+
* tokens duplicate by design — one spelling per kind, pinned by the
|
|
31
|
+
* grouping tests). */
|
|
32
|
+
export const BLOCKS_KIND = "sigrun.blocks";
|
|
33
|
+
export const BLOCKS_NOTE_KIND = "sigrun.blocks.note";
|
|
34
|
+
export const BLOCKS_ANSWER_KIND = "sigrun.blocks.answer";
|
|
35
|
+
/** The `show_blocks`/`update_blocks` tool names: a call to one of these
|
|
36
|
+
* never joins the turn's `tools` or the timeline — the block surface it
|
|
37
|
+
* created IS the visible artifact (the activity line would otherwise
|
|
38
|
+
* count its own rendering machinery as activity). */
|
|
39
|
+
const BLOCKS_TOOLS = new Set([
|
|
40
|
+
"show_blocks",
|
|
41
|
+
"update_blocks",
|
|
42
|
+
]);
|
|
27
43
|
/** Narrow one entry to its expanded-user `data` — defined exactly when
|
|
28
44
|
* the kind is {@link EXPANDED_USER_KIND} and `typed` is a non-empty
|
|
29
45
|
* string; undefined for every other shape (a plain `pi.user`, missing
|
|
@@ -105,10 +121,17 @@ export function groupTurns(entries) {
|
|
|
105
121
|
});
|
|
106
122
|
}
|
|
107
123
|
else if (block.type === "toolCall") {
|
|
124
|
+
const name = block.name ?? "";
|
|
125
|
+
// A blocks tool's call is invisible in the transcript: the
|
|
126
|
+
// `sigrun.blocks` entry it appends is the artifact. The
|
|
127
|
+
// RESULT entry still joins by callId below (matching only
|
|
128
|
+
// pending tools, so its join is a harmless no-op).
|
|
129
|
+
if (BLOCKS_TOOLS.has(name))
|
|
130
|
+
continue;
|
|
108
131
|
const call = {
|
|
109
132
|
callId: block.id ?? "",
|
|
110
|
-
name
|
|
111
|
-
preview: toolPreview(
|
|
133
|
+
name,
|
|
134
|
+
preview: toolPreview(name, block.arguments),
|
|
112
135
|
isError: false,
|
|
113
136
|
};
|
|
114
137
|
pending.tools.push(call);
|
|
@@ -131,8 +154,35 @@ export function groupTurns(entries) {
|
|
|
131
154
|
}
|
|
132
155
|
continue;
|
|
133
156
|
}
|
|
157
|
+
// U5 Task 4: the chat-blocks kinds are RUN ARTIFACTS, never
|
|
158
|
+
// standalone records (spec §8.2/§8.3). With a pending assistant
|
|
159
|
+
// turn they ride it — a boundary note mid-run appends its event
|
|
160
|
+
// and does NOT flush. With NONE (auto-compaction's pi.compaction
|
|
161
|
+
// flushed the run; the trailing note/blocks entry lands after the
|
|
162
|
+
// flush) they START one: an assistant turn with no text whose
|
|
163
|
+
// timeline still renders the block/note rows — falling through to
|
|
164
|
+
// `other` would print "unsupported entry kind".
|
|
165
|
+
// `sigrun.blocks.answer` is a user turn (§8.4: the answer submits
|
|
166
|
+
// through the normal path).
|
|
167
|
+
if (entry.kind === BLOCKS_KIND || entry.kind === BLOCKS_NOTE_KIND) {
|
|
168
|
+
pending ??= {
|
|
169
|
+
entries: [],
|
|
170
|
+
thinking: [],
|
|
171
|
+
tools: [],
|
|
172
|
+
texts: [],
|
|
173
|
+
timeline: [],
|
|
174
|
+
};
|
|
175
|
+
pending.entries.push(entry);
|
|
176
|
+
pending.timeline.push({
|
|
177
|
+
kind: entry.kind === BLOCKS_KIND ? "blocks" : "note",
|
|
178
|
+
entryId: entry.id,
|
|
179
|
+
});
|
|
180
|
+
continue;
|
|
181
|
+
}
|
|
134
182
|
flush();
|
|
135
|
-
if (entry.kind === "pi.user" ||
|
|
183
|
+
if (entry.kind === "pi.user" ||
|
|
184
|
+
entry.kind === EXPANDED_USER_KIND ||
|
|
185
|
+
entry.kind === BLOCKS_ANSWER_KIND) {
|
|
136
186
|
turns.push({ kind: "user", entry });
|
|
137
187
|
}
|
|
138
188
|
else {
|