@sema-agent/client-core 0.104.1 → 0.105.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 +90 -0
- package/README.md +11 -4
- package/dist/adapt/arms.js +14 -6
- package/dist/adapter/activeRunSelfHeal.d.ts +1 -0
- package/dist/adapter/activeRunSelfHeal.js +10 -5
- package/dist/adapter/downstream/eventToSdkMessage.js +4 -4
- package/dist/adapter/downstream/wiringManifestView.d.ts +6 -0
- package/dist/adapter/downstream/wiringManifestView.js +25 -0
- package/dist/adapter/runStream.js +5 -1
- package/dist/cloudConfigWireCaps.js +3 -0
- package/dist/compensations.js +6 -6
- package/dist/coreValuePorts.d.ts +3 -1
- package/dist/coreValuePorts.js +4 -0
- package/dist/displayUntrusted.d.ts +4 -2
- package/dist/displayUntrusted.js +45 -22
- package/dist/engineGenerationPort.d.ts +11 -2
- package/dist/engineGenerationPort.js +63 -9
- package/dist/engineHealthFacts.d.ts +5 -0
- package/dist/engineHealthFacts.js +11 -0
- package/dist/eventLeg.d.ts +1 -1
- package/dist/eventLeg.js +23 -3
- package/dist/generated/engineFactTables.d.ts +1 -0
- package/dist/generated/engineFactTables.js +1 -0
- package/dist/generated/sdkFactTables.d.ts +2 -0
- package/dist/generated/sdkFactTables.js +9 -0
- package/dist/headlessReconnectWire.js +31 -4
- package/dist/hitl/askGateWire.js +26 -2
- package/dist/hitl/frameRouter.d.ts +4 -0
- package/dist/hitl/frameRouter.js +14 -0
- package/dist/hitl/gateIdentity.d.ts +1 -0
- package/dist/hitl/gateIdentity.js +3 -0
- package/dist/hitl/planReviewCard.d.ts +21 -0
- package/dist/hitl/planReviewCard.js +229 -0
- package/dist/hitl/planReviewWire.d.ts +38 -13
- package/dist/hitl/planReviewWire.js +355 -133
- package/dist/hostSettingsSource.d.ts +1 -1
- package/dist/hostSettingsSource.js +8 -4
- package/dist/hostTurnSettings.d.ts +1 -1
- package/dist/hostTurnSettings.js +29 -10
- package/dist/i18n/messages.en.d.ts +50 -0
- package/dist/i18n/messages.en.js +50 -0
- package/dist/i18n/messages.zh.js +51 -0
- package/dist/index.d.ts +15 -3
- package/dist/index.js +9 -1
- package/dist/mcpTransportKindsCapability.d.ts +18 -0
- package/dist/mcpTransportKindsCapability.js +75 -0
- package/dist/mcpWireCaps.d.ts +4 -1
- package/dist/mcpWireCaps.js +24 -13
- package/dist/notifications.d.ts +9 -0
- package/dist/notifications.js +11 -2
- package/dist/ownKey.d.ts +4 -0
- package/dist/ownKey.js +8 -0
- package/dist/permissionModeWriteCapability.d.ts +13 -0
- package/dist/permissionModeWriteCapability.js +35 -0
- package/dist/permissionsAllowGrantCapability.d.ts +3 -0
- package/dist/permissionsAllowGrantCapability.js +26 -3
- package/dist/permissionsDeclaration.d.ts +23 -1
- package/dist/permissionsDeclaration.js +60 -30
- package/dist/planReviewCardFacts.d.ts +116 -0
- package/dist/planReviewCardFacts.js +469 -0
- package/dist/reattachWindow.d.ts +8 -1
- package/dist/reattachWindow.js +44 -12
- package/dist/runFollow.d.ts +30 -0
- package/dist/runFollow.js +117 -0
- package/dist/runLedgerSnapshot.d.ts +20 -0
- package/dist/runLedgerSnapshot.js +272 -0
- package/dist/runPermissionModeSwitch.d.ts +57 -0
- package/dist/runPermissionModeSwitch.js +135 -0
- package/dist/runReattachPolicy.d.ts +13 -5
- package/dist/runReattachPolicy.js +58 -23
- package/dist/seam.d.ts +3 -0
- package/dist/seam.js +1 -1
- package/dist/seatContract.js +1 -0
- package/dist/sessionMemoryErase.d.ts +1 -1
- package/dist/sessionMemoryErase.js +5 -0
- package/dist/sessionMemoryStatus.d.ts +27 -1
- package/dist/sessionMemoryStatus.js +117 -0
- package/dist/sseIdleTriage.d.ts +3 -1
- package/dist/sseIdleTriage.js +19 -4
- package/dist/subagent/engineSubagentTail.js +2 -0
- package/dist/subagentContentStore.d.ts +3 -0
- package/dist/subagentContentStore.js +10 -1
- package/dist/turnFailurePresentation.d.ts +5 -2
- package/dist/turnFailurePresentation.js +12 -4
- package/docs/INTEGRATION-CLIENTS.md +493 -10
- package/package.json +2 -2
package/dist/mcpWireCaps.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { McpServerSpec } from '@sema-agent/sdk';
|
|
2
2
|
import type { McpEntryKeyIssue } from './generated/settingsSchemaTypes.js';
|
|
3
|
+
import { type McpTransportKindsReading } from './mcpTransportKindsCapability.js';
|
|
3
4
|
import type { UiLanguage } from './env/uiLanguage.js';
|
|
4
5
|
export type { McpServerSpec };
|
|
5
6
|
export interface McpConfigLike {
|
|
@@ -16,6 +17,7 @@ export interface McpConfigLike {
|
|
|
16
17
|
toolFaces?: unknown;
|
|
17
18
|
toolAxes?: unknown;
|
|
18
19
|
startupTimeoutMs?: number;
|
|
20
|
+
sessionMetaKey?: string;
|
|
19
21
|
}
|
|
20
22
|
export type McpTransportEntryLike = Pick<McpConfigLike, 'type' | 'command' | 'args' | 'env' | 'url' | 'headers' | 'principalHeader' | 'assertionHeader'>;
|
|
21
23
|
export type McpTransportVerdict = {
|
|
@@ -72,7 +74,7 @@ export type McpAllowToolsVerdict = {
|
|
|
72
74
|
readonly problem: string;
|
|
73
75
|
};
|
|
74
76
|
export declare function mcpAllowToolsVerdict(read: () => {} | null | undefined, keyPrefix?: string): McpAllowToolsVerdict;
|
|
75
|
-
export type McpConfigDropReason = 'name_blank' | 'name_edge_whitespace' | 'name_collides_after_trim' | 'name_too_long' | 'name_reserved' | 'transport_invalid' | 'identity_headers_both_set' | 'tool_axes_invalid' | 'tool_faces_invalid' | 'elicitation_invalid' | 'allow_tools_invalid' | 'entry_not_object' | 'entry_unreadable';
|
|
77
|
+
export type McpConfigDropReason = 'name_blank' | 'name_edge_whitespace' | 'name_collides_after_trim' | 'name_too_long' | 'name_reserved' | 'transport_invalid' | 'identity_headers_both_set' | 'tool_axes_invalid' | 'tool_faces_invalid' | 'elicitation_invalid' | 'allow_tools_invalid' | 'entry_not_object' | 'entry_unreadable' | 'transport_not_advertised';
|
|
76
78
|
export type McpServerNameUnusableReason = 'name_blank' | 'name_edge_whitespace' | 'name_collides_after_trim' | 'name_too_long' | 'name_reserved';
|
|
77
79
|
export type McpServerNameVerdict = {
|
|
78
80
|
readonly usable: true;
|
|
@@ -100,6 +102,7 @@ export interface McpConfigProjection {
|
|
|
100
102
|
}
|
|
101
103
|
export interface ProjectMcpConfigsOptions {
|
|
102
104
|
readonly sourceLabel?: (serverName: string) => string | undefined;
|
|
105
|
+
readonly transportKinds?: McpTransportKindsReading;
|
|
103
106
|
readonly lang?: UiLanguage;
|
|
104
107
|
}
|
|
105
108
|
export declare function projectMcpConfigs(servers: Record<string, McpConfigLike> | undefined, opts?: ProjectMcpConfigsOptions): McpConfigProjection;
|
package/dist/mcpWireCaps.js
CHANGED
|
@@ -4,14 +4,16 @@ import { appendOwnElement, guardedRead } from './ownKey.js';
|
|
|
4
4
|
import { readMcpToolAxes, readToolFaces } from './coreValuePorts.js';
|
|
5
5
|
import { capForDisplay } from './fleetTaskDesc.js';
|
|
6
6
|
import { msg, renderMessage } from './i18n/translate.js';
|
|
7
|
+
import { mcpTransportNotAdvertised } from './mcpTransportKindsCapability.js';
|
|
7
8
|
const _mcpServerSpecPin = true;
|
|
8
9
|
void _mcpServerSpecPin;
|
|
9
|
-
function transparentKeys(startupTimeoutMs, axes, faces, elicitation) {
|
|
10
|
+
function transparentKeys(startupTimeoutMs, axes, faces, elicitation, sessionMetaKey) {
|
|
10
11
|
return {
|
|
11
12
|
...(faces !== undefined ? { toolFaces: faces } : {}),
|
|
12
13
|
...(axes !== undefined ? { toolAxes: axes } : {}),
|
|
13
14
|
...(elicitation !== undefined ? { elicitation } : {}),
|
|
14
15
|
...(startupTimeoutMs !== undefined ? { startupTimeoutMs } : {}),
|
|
16
|
+
...(sessionMetaKey !== undefined ? { sessionMetaKey } : {}),
|
|
15
17
|
};
|
|
16
18
|
}
|
|
17
19
|
export function readMcpHttpIdentityHeaders(transport) {
|
|
@@ -36,7 +38,7 @@ function entryShapeOf(c) {
|
|
|
36
38
|
function readEntryOwnKeys(c) {
|
|
37
39
|
try {
|
|
38
40
|
const allowTools = c.allowTools;
|
|
39
|
-
return { toolAxes: c.toolAxes, elicitation: c.elicitation, allowTools, startupTimeoutMs: c.startupTimeoutMs };
|
|
41
|
+
return { toolAxes: c.toolAxes, elicitation: c.elicitation, allowTools, startupTimeoutMs: c.startupTimeoutMs, sessionMetaKey: c.sessionMetaKey };
|
|
40
42
|
}
|
|
41
43
|
catch {
|
|
42
44
|
return undefined;
|
|
@@ -153,7 +155,7 @@ function specOf(name, c, own, axes, faces, elicitation, allowTools) {
|
|
|
153
155
|
name,
|
|
154
156
|
transport: t.transport,
|
|
155
157
|
...(allowTools !== undefined ? { allowTools } : {}),
|
|
156
|
-
...transparentKeys(own.startupTimeoutMs, axes, faces, elicitation),
|
|
158
|
+
...transparentKeys(own.startupTimeoutMs, axes, faces, elicitation, own.sessionMetaKey),
|
|
157
159
|
};
|
|
158
160
|
return { kind: 'ok', spec, lost: t.lost };
|
|
159
161
|
}
|
|
@@ -305,7 +307,7 @@ const DROP_LABEL_SHOWN = 140;
|
|
|
305
307
|
const DROP_PROBLEM_SHOWN = 300;
|
|
306
308
|
const DROP_BAD_NAME_SHOWN = 160;
|
|
307
309
|
const DEGRADED_LOST_SHOWN = 400;
|
|
308
|
-
function dropNotice(name, reason, problem, opts, path) {
|
|
310
|
+
function dropNotice(name, reason, problem, opts, path, transportKind) {
|
|
309
311
|
let label;
|
|
310
312
|
try {
|
|
311
313
|
label = opts?.sourceLabel?.(name);
|
|
@@ -332,15 +334,17 @@ function dropNotice(name, reason, problem, opts, path) {
|
|
|
332
334
|
? msg('config.mcp.identityHeadersBothSet', { name: quoted, where })
|
|
333
335
|
: reason === 'transport_invalid'
|
|
334
336
|
? msg('config.mcp.transportInvalid', { name: quoted, where, path: capForDisplay(path ?? 'transport', DROP_NAME_SHOWN), problem: capForDisplay(problem ?? '', DROP_PROBLEM_SHOWN) })
|
|
335
|
-
: reason === '
|
|
336
|
-
? msg('config.mcp.
|
|
337
|
-
: reason === '
|
|
338
|
-
? msg('config.mcp.
|
|
339
|
-
: reason === '
|
|
340
|
-
? msg('config.mcp.
|
|
341
|
-
: reason === '
|
|
342
|
-
? msg('config.mcp.
|
|
343
|
-
:
|
|
337
|
+
: reason === 'transport_not_advertised'
|
|
338
|
+
? msg('config.mcp.transportNotAdvertised', { name: quoted, where, kind: capForDisplay(transportKind ?? '', DROP_NAME_SHOWN), supported: problem !== undefined && problem.length > 0 ? problem : renderMessage(msg('config.mcp.transportsNone'), opts?.lang) })
|
|
339
|
+
: reason === 'name_blank'
|
|
340
|
+
? msg('config.mcp.nameBlank', { name: badName(), where })
|
|
341
|
+
: reason === 'name_collides_after_trim'
|
|
342
|
+
? msg('config.mcp.nameCollides', { name: badName(), where })
|
|
343
|
+
: reason === 'name_too_long'
|
|
344
|
+
? msg('config.mcp.nameTooLong', { name: badName(), where })
|
|
345
|
+
: reason === 'name_reserved'
|
|
346
|
+
? msg('config.mcp.nameReserved', { name: badName(), where })
|
|
347
|
+
: msg('config.mcp.nameEdgeWhitespace', { name: badName(), where });
|
|
344
348
|
return renderMessage(ref, opts?.lang);
|
|
345
349
|
}
|
|
346
350
|
function degradedNotice(name, lost, opts) {
|
|
@@ -381,6 +385,13 @@ export function projectMcpConfigs(servers, opts) {
|
|
|
381
385
|
}
|
|
382
386
|
const out = slotUnreadable ? { spec: null, entryUnreadable: true } : mcpConfigEntry(name, c);
|
|
383
387
|
if (out.spec !== null) {
|
|
388
|
+
const kind = out.spec.transport.kind;
|
|
389
|
+
const supported = opts?.transportKinds === undefined ? undefined : mcpTransportNotAdvertised(opts.transportKinds, kind);
|
|
390
|
+
if (supported !== undefined) {
|
|
391
|
+
const listed = supported.join(', ');
|
|
392
|
+
dropped.push({ name, reason: 'transport_not_advertised', path: 'transport.kind', problem: listed, notice: dropNotice(name, 'transport_not_advertised', listed, opts, 'transport.kind', kind) });
|
|
393
|
+
continue;
|
|
394
|
+
}
|
|
384
395
|
specs.push(out.spec);
|
|
385
396
|
if (out.transportLost !== undefined)
|
|
386
397
|
degraded.push({ name, lost: Object.freeze([...out.transportLost]), notice: degradedNotice(name, out.transportLost, opts) });
|
package/dist/notifications.d.ts
CHANGED
|
@@ -42,6 +42,14 @@ export declare function taskNotificationDedupKey(n: {
|
|
|
42
42
|
seq?: number | string;
|
|
43
43
|
taskType?: string;
|
|
44
44
|
}): string;
|
|
45
|
+
export interface TaskNotificationRenderKeyInput {
|
|
46
|
+
taskId: string;
|
|
47
|
+
status: string;
|
|
48
|
+
seq?: number | string;
|
|
49
|
+
taskType?: string;
|
|
50
|
+
completionId?: string;
|
|
51
|
+
}
|
|
52
|
+
export declare function taskNotificationRenderKeys(n: TaskNotificationRenderKeyInput): readonly [selfKey: string, ...completionKey: string[]];
|
|
45
53
|
export declare function taskNotificationDedupKeyFromWire(raw: Record<string, unknown>): string;
|
|
46
54
|
export interface TranscriptNotificationSeeds {
|
|
47
55
|
renderedKeys: string[];
|
|
@@ -120,6 +128,7 @@ export interface EngineWorkflowCompletion {
|
|
|
120
128
|
runId: string;
|
|
121
129
|
status: 'completed' | 'failed';
|
|
122
130
|
summary: string;
|
|
131
|
+
completionId?: string;
|
|
123
132
|
}
|
|
124
133
|
export declare function enqueueBgChildNotification(raw: {
|
|
125
134
|
taskId: string;
|
package/dist/notifications.js
CHANGED
|
@@ -5,7 +5,7 @@ import { panelEndFact } from './panelReduce.js';
|
|
|
5
5
|
import { isCoreTaskNotificationTerminal, isCoreTaskNotificationTerminalNotSuccess } from './terminalStatuses.js';
|
|
6
6
|
import { cycleSeqValue } from './cycleOrder.js';
|
|
7
7
|
import { CORE_ENGINE_AUTHORITY_ENVELOPE_TAGS } from './generated/engineFactTables.js';
|
|
8
|
-
import { guardedRead, ownSnapshot, ownSnapshotRecord, ownSnapshotTyped, readOwnDataValue } from './ownKey.js';
|
|
8
|
+
import { guardedRead, ownSnapshot, ownSnapshotRecord, ownSnapshotTyped, readOwnDataValue, workflowCompletionExtrasOf } from './ownKey.js';
|
|
9
9
|
function escapeXml(v) {
|
|
10
10
|
return v.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
|
|
11
11
|
}
|
|
@@ -244,6 +244,14 @@ export function taskNotificationDedupKey(n) {
|
|
|
244
244
|
const lane = n.taskType === 'external' ? 'external:' : '';
|
|
245
245
|
return `${lane}${n.taskId}:${n.status}${seq ? `:${seq}` : ''}`;
|
|
246
246
|
}
|
|
247
|
+
const COMPLETION_KEY_PREFIX = 'completion:';
|
|
248
|
+
export function taskNotificationRenderKeys(n) {
|
|
249
|
+
const own = taskNotificationDedupKey(n);
|
|
250
|
+
const id = n.completionId;
|
|
251
|
+
if (n.taskType === 'external' || !isCoreTaskNotificationTerminal(n.status) || typeof id !== 'string' || id.length === 0)
|
|
252
|
+
return [own];
|
|
253
|
+
return [own, `${COMPLETION_KEY_PREFIX}${id}`];
|
|
254
|
+
}
|
|
247
255
|
export function taskNotificationDedupKeyFromWire(raw) {
|
|
248
256
|
const n = ownSnapshotRecord(raw) ?? {};
|
|
249
257
|
return taskNotificationDedupKey({
|
|
@@ -830,6 +838,7 @@ function enqueueBgChild(n, identity) {
|
|
|
830
838
|
...identity,
|
|
831
839
|
}));
|
|
832
840
|
}
|
|
841
|
+
const completionIdOnly = (x) => (x.completionId !== undefined ? { completionId: x.completionId } : {});
|
|
833
842
|
export function enqueueEngineWorkflowNotification(raw) {
|
|
834
843
|
const c = ownSnapshotRecord(raw);
|
|
835
844
|
if (c === undefined) {
|
|
@@ -843,7 +852,7 @@ export function enqueueEngineWorkflowNotification(raw) {
|
|
|
843
852
|
}
|
|
844
853
|
markRunNotified(c.runId);
|
|
845
854
|
cardEnqueuedRunIds.add(c.runId);
|
|
846
|
-
port()?.enqueuePendingNotification(taskNotificationQueueCommand({ taskId: c.runId, taskType: 'workflow', status: c.status, summary: c.summary }));
|
|
855
|
+
port()?.enqueuePendingNotification(taskNotificationQueueCommand({ taskId: c.runId, taskType: 'workflow', status: c.status, summary: c.summary, ...completionIdOnly(workflowCompletionExtrasOf(c)) }));
|
|
847
856
|
}
|
|
848
857
|
export function _resetEngineTaskNotificationForTest() {
|
|
849
858
|
notifiedRunIds.clear();
|
package/dist/ownKey.d.ts
CHANGED
|
@@ -71,3 +71,7 @@ export interface SampledRecord {
|
|
|
71
71
|
export declare function ownSnapshotWhenTyped<V>(value: V, word: string): SampledJson;
|
|
72
72
|
export declare function ownSnapshotTyped<T extends object>(value: T): T | undefined;
|
|
73
73
|
export declare function ownSnapshotRecord<V>(value: V): SampledRecord | undefined;
|
|
74
|
+
export declare function workflowCompletionExtrasOf(c: object): {
|
|
75
|
+
completionId?: string;
|
|
76
|
+
diagnostics?: string;
|
|
77
|
+
};
|
package/dist/ownKey.js
CHANGED
|
@@ -440,3 +440,11 @@ function sampleObject(src, depth, run) {
|
|
|
440
440
|
run.path.delete(src);
|
|
441
441
|
}
|
|
442
442
|
}
|
|
443
|
+
export function workflowCompletionExtrasOf(c) {
|
|
444
|
+
const completionId = guardedRead(() => readOwnDataNonEmptyString(c, 'completionId'));
|
|
445
|
+
const diagnostics = guardedRead(() => readOwnDataNonEmptyString(c, 'diagnostics'));
|
|
446
|
+
return {
|
|
447
|
+
...(completionId !== undefined ? { completionId } : {}),
|
|
448
|
+
...(diagnostics !== undefined ? { diagnostics } : {}),
|
|
449
|
+
};
|
|
450
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export type PermissionModeWriteReading = {
|
|
2
|
+
kind: 'unobserved';
|
|
3
|
+
} | {
|
|
4
|
+
kind: 'unreadable';
|
|
5
|
+
} | {
|
|
6
|
+
kind: 'not_reported';
|
|
7
|
+
} | {
|
|
8
|
+
kind: 'present';
|
|
9
|
+
value: boolean;
|
|
10
|
+
};
|
|
11
|
+
export declare function projectPermissionModeWriteCapability(caps: unknown): PermissionModeWriteReading | undefined;
|
|
12
|
+
export declare function observedPermissionModeWrite(baseUrl?: string | undefined): PermissionModeWriteReading;
|
|
13
|
+
export declare function permissionModeWriteAvailable(reading: PermissionModeWriteReading | undefined): boolean;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { engineWireTargetBaseUrl } from './engineWireTarget.js';
|
|
2
|
+
import { createEngineCapReader } from './engineCapReader.js';
|
|
3
|
+
import { ownDataField, readOwnDataValue } from './ownKey.js';
|
|
4
|
+
export function projectPermissionModeWriteCapability(caps) {
|
|
5
|
+
try {
|
|
6
|
+
if (caps === null || typeof caps !== 'object' || Array.isArray(caps))
|
|
7
|
+
return undefined;
|
|
8
|
+
const v = ownDataField(caps, 'permissionModeWrite');
|
|
9
|
+
if (v === undefined)
|
|
10
|
+
return { kind: 'not_reported' };
|
|
11
|
+
return typeof v === 'boolean' ? { kind: 'present', value: v } : undefined;
|
|
12
|
+
}
|
|
13
|
+
catch {
|
|
14
|
+
return undefined;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
const reader = createEngineCapReader({
|
|
18
|
+
name: 'permissionModeWrite',
|
|
19
|
+
project: projectPermissionModeWriteCapability,
|
|
20
|
+
});
|
|
21
|
+
export function observedPermissionModeWrite(baseUrl = engineWireTargetBaseUrl()) {
|
|
22
|
+
return reader.observed(baseUrl);
|
|
23
|
+
}
|
|
24
|
+
export function permissionModeWriteAvailable(reading) {
|
|
25
|
+
try {
|
|
26
|
+
if (reading === null || reading === undefined || typeof reading !== 'object' || Array.isArray(reading))
|
|
27
|
+
return false;
|
|
28
|
+
if (readOwnDataValue(reading, 'kind') !== 'present')
|
|
29
|
+
return false;
|
|
30
|
+
return readOwnDataValue(reading, 'value') === true;
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
export declare const RULE_LAYERS_CAP_LEAF: "layers";
|
|
1
2
|
export type PermissionsAllowGrantReading = {
|
|
2
3
|
kind: 'unobserved';
|
|
3
4
|
} | {
|
|
@@ -8,10 +9,12 @@ export type PermissionsAllowGrantReading = {
|
|
|
8
9
|
kind: 'present';
|
|
9
10
|
grants: boolean;
|
|
10
11
|
layer?: boolean;
|
|
12
|
+
layers?: boolean;
|
|
11
13
|
};
|
|
12
14
|
export declare function projectPermissionsAllowGrantCapability(caps: unknown): PermissionsAllowGrantReading | undefined;
|
|
13
15
|
export declare function observedPermissionsAllowGrant(baseUrl?: string | undefined): PermissionsAllowGrantReading;
|
|
14
16
|
export declare function permissionsAllowGrantAvailable(reading: PermissionsAllowGrantReading): boolean;
|
|
15
17
|
export declare function allowWireForm(reading: PermissionsAllowGrantReading): 'layer' | 'grant' | 'off';
|
|
16
18
|
export declare function allowRulesGate(reading: PermissionsAllowGrantReading, settingsRead: 'clean' | 'partial' | 'unreadable' | undefined): 'open' | 'capability_off' | 'settings_read_not_clean';
|
|
19
|
+
export declare function permissionRuleLayersOn(reading: PermissionsAllowGrantReading | undefined): boolean;
|
|
17
20
|
export declare function permissionsAllowGrantDoctorDetail(reading: PermissionsAllowGrantReading, settingsRead?: 'clean' | 'partial' | 'unreadable'): string;
|
|
@@ -4,6 +4,7 @@ import { ownDataField } from './ownKey.js';
|
|
|
4
4
|
import { hasCoreValuePort } from './coreValuePorts.js';
|
|
5
5
|
const ALLOW_GRANT_CAP_PATH = ['settings', 'permissions', 'allowGrant'];
|
|
6
6
|
const ALLOW_LAYER_CAP_LEAF = 'allowLayer';
|
|
7
|
+
export const RULE_LAYERS_CAP_LEAF = 'layers';
|
|
7
8
|
export function projectPermissionsAllowGrantCapability(caps) {
|
|
8
9
|
try {
|
|
9
10
|
if (caps === null || typeof caps !== 'object' || Array.isArray(caps))
|
|
@@ -18,9 +19,21 @@ export function projectPermissionsAllowGrantCapability(caps) {
|
|
|
18
19
|
if (typeof v !== 'boolean')
|
|
19
20
|
return undefined;
|
|
20
21
|
const layer = ownDataField(node, ALLOW_LAYER_CAP_LEAF);
|
|
21
|
-
if (layer
|
|
22
|
-
return
|
|
23
|
-
|
|
22
|
+
if (layer !== undefined && typeof layer !== 'boolean')
|
|
23
|
+
return undefined;
|
|
24
|
+
let layers;
|
|
25
|
+
try {
|
|
26
|
+
layers = ownDataField(node, RULE_LAYERS_CAP_LEAF);
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
layers = undefined;
|
|
30
|
+
}
|
|
31
|
+
return {
|
|
32
|
+
kind: 'present',
|
|
33
|
+
grants: v,
|
|
34
|
+
...(layer !== undefined ? { layer } : {}),
|
|
35
|
+
...(typeof layers === 'boolean' ? { layers } : {}),
|
|
36
|
+
};
|
|
24
37
|
}
|
|
25
38
|
if (v === null || typeof v !== 'object' || Array.isArray(v))
|
|
26
39
|
return undefined;
|
|
@@ -61,6 +74,16 @@ export function allowRulesGate(reading, settingsRead) {
|
|
|
61
74
|
return 'capability_off';
|
|
62
75
|
return settingsRead === 'clean' ? 'open' : 'settings_read_not_clean';
|
|
63
76
|
}
|
|
77
|
+
export function permissionRuleLayersOn(reading) {
|
|
78
|
+
try {
|
|
79
|
+
if (reading === null || typeof reading !== 'object' || Array.isArray(reading))
|
|
80
|
+
return false;
|
|
81
|
+
return ownDataField(reading, 'kind') === 'present' && ownDataField(reading, RULE_LAYERS_CAP_LEAF) === true;
|
|
82
|
+
}
|
|
83
|
+
catch {
|
|
84
|
+
return false;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
64
87
|
function ownReadingField(reading, key) {
|
|
65
88
|
try {
|
|
66
89
|
if (reading === null || typeof reading !== 'object' || Array.isArray(reading))
|
|
@@ -39,6 +39,24 @@ export interface PermissionsDeclarationBuckets {
|
|
|
39
39
|
export interface PermissionsDeclaration {
|
|
40
40
|
permissions?: PermissionsDeclarationBuckets;
|
|
41
41
|
}
|
|
42
|
+
export type SettingsRuleLayerOrigin = keyof typeof SETTINGS_SOURCE_TIERS;
|
|
43
|
+
export interface TightenRuleContribution {
|
|
44
|
+
readonly rule: string;
|
|
45
|
+
readonly origin: WireRuleOrigin;
|
|
46
|
+
}
|
|
47
|
+
export interface LayeredTightenEntry {
|
|
48
|
+
readonly rule: string;
|
|
49
|
+
readonly layer: SettingsRuleLayerOrigin;
|
|
50
|
+
}
|
|
51
|
+
export type TightenWireEntry = string | LayeredTightenEntry;
|
|
52
|
+
export interface HostPermissionsBuckets {
|
|
53
|
+
deny?: TightenWireEntry[];
|
|
54
|
+
ask?: TightenWireEntry[];
|
|
55
|
+
allow?: string[];
|
|
56
|
+
}
|
|
57
|
+
export interface HostPermissionsDeclaration {
|
|
58
|
+
permissions?: HostPermissionsBuckets;
|
|
59
|
+
}
|
|
42
60
|
export declare const PERMISSIONS_KEPT_REASONS: readonly ["allow_capability_off", "settings_read_not_clean", "empty_rule", "paren_in_tool_name", "retired_tool_name", "unprefixed_namespaced_name", "incomplete_protocol_name", "unminted_protocol_segment", "home_relative_path", "compile_refused", "content_form_not_grantable", "content_form_never_matches", "judge_failed", "duplicate", "voids_allow_list"];
|
|
43
61
|
export type PermissionsKeptReason = (typeof PERMISSIONS_KEPT_REASONS)[number];
|
|
44
62
|
export interface PermissionsKeptRule {
|
|
@@ -86,8 +104,12 @@ export interface AssembledRuleRow {
|
|
|
86
104
|
readonly origin?: WireRuleOrigin;
|
|
87
105
|
readonly fate: AssembledRuleFate;
|
|
88
106
|
}
|
|
107
|
+
export interface LayeredTightenSources {
|
|
108
|
+
readonly deny: readonly TightenRuleContribution[];
|
|
109
|
+
readonly ask: readonly TightenRuleContribution[];
|
|
110
|
+
}
|
|
89
111
|
export declare function permissionsDeclaration(input: PermissionsDeclarationInput, caps: PermissionsAllowGrantReading): PermissionsDeclaration;
|
|
90
|
-
export declare function projectPermissionsDeclaration(input: PermissionsDeclarationInput, caps: PermissionsAllowGrantReading, projection: RulePortsProjection):
|
|
112
|
+
export declare function projectPermissionsDeclaration(input: PermissionsDeclarationInput, caps: PermissionsAllowGrantReading, projection: RulePortsProjection, layered?: LayeredTightenSources): HostPermissionsDeclaration;
|
|
91
113
|
export declare function permissionsDeclarationReport(input: PermissionsDeclarationInput, caps: PermissionsAllowGrantReading): PermissionsDeclarationReport;
|
|
92
114
|
export declare function assembledRuleRows(input: PermissionsDeclarationInput, caps: PermissionsAllowGrantReading): readonly AssembledRuleRow[];
|
|
93
115
|
export interface PermissionsAllowKeptFacts {
|
|
@@ -244,45 +244,60 @@ function allowListVoiders(sent) {
|
|
|
244
244
|
return 'unknown';
|
|
245
245
|
}
|
|
246
246
|
}
|
|
247
|
-
function judgeBucket(raw, behavior, originOf, cli, settingsGate, allowForm = 'grant', dropWire, owned = [], serverVersion) {
|
|
247
|
+
function judgeBucket(raw, behavior, originOf, cli, settingsGate, allowForm = 'grant', dropWire, owned = [], serverVersion, layeredFrom) {
|
|
248
248
|
const firstSlot = new Map();
|
|
249
249
|
for (const s of raw)
|
|
250
250
|
if (!firstSlot.has(s.rule))
|
|
251
251
|
firstSlot.set(s.rule, s);
|
|
252
252
|
const groups = WIRE_RULE_TRUST_ORDER.map(() => []);
|
|
253
253
|
const rankOf = (origin) => WIRE_RULE_TRUST_ORDER.indexOf(origin);
|
|
254
|
-
|
|
255
|
-
|
|
254
|
+
const pairs = layeredFrom !== undefined ? layeredFrom.map((c) => [c.rule, c.origin]) : originOf;
|
|
255
|
+
const known = new Set();
|
|
256
|
+
if (pairs !== undefined) {
|
|
257
|
+
for (const [r, origin] of pairs) {
|
|
256
258
|
const rank = rankOf(origin);
|
|
257
|
-
if (rank >= 0 && typeof r === 'string' && firstSlot.has(r))
|
|
259
|
+
if (rank >= 0 && typeof r === 'string' && firstSlot.has(r)) {
|
|
258
260
|
appendOwnElement(groups[rank], r);
|
|
261
|
+
known.add(r);
|
|
262
|
+
}
|
|
259
263
|
}
|
|
260
264
|
}
|
|
261
|
-
const unknownOrigin = [...firstSlot.keys()].filter((r) =>
|
|
265
|
+
const unknownOrigin = [...firstSlot.keys()].filter((r) => !known.has(r));
|
|
262
266
|
const fateOfRule = new Map();
|
|
263
267
|
const sent = [];
|
|
268
|
+
const sentLayers = [];
|
|
269
|
+
const sentIds = [];
|
|
264
270
|
const winnerOf = new Map();
|
|
265
271
|
const cliFates = cli === undefined ? [] : [...cli.fates];
|
|
266
|
-
const
|
|
272
|
+
const layerOf = (origin) => layeredFrom !== undefined && origin !== undefined && Object.hasOwn(SETTINGS_SOURCE_TIERS, origin) ? origin : undefined;
|
|
273
|
+
const place = (slot, fate, origin) => {
|
|
267
274
|
if (!fate.sent)
|
|
268
275
|
return fate;
|
|
269
276
|
const dropped = dropWire?.(fate.wire);
|
|
270
277
|
if (dropped !== undefined)
|
|
271
278
|
return kept(dropped);
|
|
272
|
-
|
|
279
|
+
const layer = layerOf(origin);
|
|
280
|
+
const id = layeredFrom === undefined ? fate.wire : JSON.stringify([layer ?? null, fate.wire]);
|
|
281
|
+
if (winnerOf.has(id))
|
|
273
282
|
return kept('duplicate');
|
|
274
|
-
winnerOf.set(
|
|
283
|
+
winnerOf.set(id, slot);
|
|
275
284
|
appendOwnElement(sent, fate.wire);
|
|
285
|
+
appendOwnElement(sentLayers, layer);
|
|
286
|
+
appendOwnElement(sentIds, id);
|
|
276
287
|
return fate;
|
|
277
288
|
};
|
|
278
289
|
const appendGroup = (group, origin) => {
|
|
279
290
|
const held = settingsGate !== undefined && !outsideSettingsFiles(origin);
|
|
280
291
|
const fates = held ? group.map(() => kept(settingsGate)) : judgeList(group, behavior, allowForm, serverVersion);
|
|
281
|
-
group.forEach((r, k) =>
|
|
292
|
+
group.forEach((r, k) => {
|
|
293
|
+
const f = place(firstSlot.get(r), fates[k], origin);
|
|
294
|
+
if (!fateOfRule.has(r))
|
|
295
|
+
fateOfRule.set(r, f);
|
|
296
|
+
});
|
|
282
297
|
};
|
|
283
298
|
WIRE_RULE_TRUST_ORDER.forEach((origin, rank) => {
|
|
284
299
|
if (origin === 'cliArg' && cli !== undefined)
|
|
285
|
-
cli.slots.forEach((s, k) => (cliFates[k] = place(s, cliFates[k])));
|
|
300
|
+
cli.slots.forEach((s, k) => (cliFates[k] = place(s, cliFates[k], 'cliArg')));
|
|
286
301
|
appendGroup(groups[rank], origin);
|
|
287
302
|
});
|
|
288
303
|
const ownedFates = owned.map((fate, k) => place({ rule: fate.sent ? fate.wire : '', list: 'allow', at: -1 - k }, fate));
|
|
@@ -293,7 +308,7 @@ function judgeBucket(raw, behavior, originOf, cli, settingsGate, allowForm = 'gr
|
|
|
293
308
|
return f;
|
|
294
309
|
return f.sent || f.reason === 'duplicate' ? kept('duplicate') : f;
|
|
295
310
|
});
|
|
296
|
-
return { sent, fates: [...fates, ...cliFates], winnerOf, ownedFates };
|
|
311
|
+
return { sent, sentLayers, sentIds, fates: [...fates, ...cliFates], winnerOf, ownedFates };
|
|
297
312
|
}
|
|
298
313
|
function clientOwnedFate(server) {
|
|
299
314
|
if (server.length === 0)
|
|
@@ -312,7 +327,7 @@ function ownField(obj, key, where) {
|
|
|
312
327
|
throw malformed(`${WHO}: ${where}${key} 只在原型链上,不是自有字段 —— 拒绝读取(请交普通对象)。`);
|
|
313
328
|
return undefined;
|
|
314
329
|
}
|
|
315
|
-
function ruleList(v, key) {
|
|
330
|
+
function ruleList(v, key, tighten = false) {
|
|
316
331
|
if (v === undefined || v === null)
|
|
317
332
|
return undefined;
|
|
318
333
|
if (!Array.isArray(v))
|
|
@@ -326,6 +341,9 @@ function ruleList(v, key) {
|
|
|
326
341
|
continue;
|
|
327
342
|
}
|
|
328
343
|
const r = v[i];
|
|
344
|
+
if (tighten && r !== null && typeof r === 'object') {
|
|
345
|
+
throw malformed(`${WHO}: ${key}[${i}] 是对象(带出处层的条目形)—— 本口只收规则串:收紧方向的条目不静默丢,拒绝装配(带层条目由宿主设置入口在能力位恰真时铸)。`);
|
|
346
|
+
}
|
|
329
347
|
if (typeof r === 'string')
|
|
330
348
|
appendOwnElement(out, r);
|
|
331
349
|
}
|
|
@@ -348,17 +366,17 @@ function originMap(v, key) {
|
|
|
348
366
|
return snap;
|
|
349
367
|
}
|
|
350
368
|
const slotsOf = (list, name) => (list ?? []).map((rule, at) => ({ rule, list: name, at }));
|
|
351
|
-
function assemble(input, caps, projection) {
|
|
369
|
+
function assemble(input, caps, projection, layered) {
|
|
352
370
|
const outer = activeRulePortsProjection;
|
|
353
371
|
activeRulePortsProjection = projection;
|
|
354
372
|
try {
|
|
355
|
-
return assembleNow(input, caps);
|
|
373
|
+
return assembleNow(input, caps, layered);
|
|
356
374
|
}
|
|
357
375
|
finally {
|
|
358
376
|
activeRulePortsProjection = outer;
|
|
359
377
|
}
|
|
360
378
|
}
|
|
361
|
-
function assembleNow(input, caps) {
|
|
379
|
+
function assembleNow(input, caps, layered) {
|
|
362
380
|
let read;
|
|
363
381
|
try {
|
|
364
382
|
read = readAssemblyInput(input, caps);
|
|
@@ -367,7 +385,7 @@ function assembleNow(input, caps) {
|
|
|
367
385
|
throw asAssemblyRefusal(e, 'input_malformed', WHO);
|
|
368
386
|
}
|
|
369
387
|
try {
|
|
370
|
-
return judgeAssembly(read, caps);
|
|
388
|
+
return judgeAssembly(read, caps, layered);
|
|
371
389
|
}
|
|
372
390
|
catch (e) {
|
|
373
391
|
throw asAssemblyRefusal(e, 'judge_unanswerable', WHO);
|
|
@@ -377,10 +395,10 @@ function readAssemblyInput(input, caps) {
|
|
|
377
395
|
if (input === null || typeof input !== 'object' || Array.isArray(input)) {
|
|
378
396
|
throw malformed(`${WHO}: 入参必须是对象 —— 收到 ${input === null ? 'null' : Array.isArray(input) ? 'array' : `typeof=${typeof input}`}。`);
|
|
379
397
|
}
|
|
380
|
-
const deny = ruleList(ownField(input, 'deny', ''), 'deny');
|
|
381
|
-
const ask = ruleList(ownField(input, 'ask', ''), 'ask');
|
|
398
|
+
const deny = ruleList(ownField(input, 'deny', ''), 'deny', true);
|
|
399
|
+
const ask = ruleList(ownField(input, 'ask', ''), 'ask', true);
|
|
382
400
|
const allow = ruleList(ownField(input, 'allow', ''), 'allow');
|
|
383
|
-
const cliDeny = ruleList(ownField(input, 'cliDeny', ''), 'cliDeny');
|
|
401
|
+
const cliDeny = ruleList(ownField(input, 'cliDeny', ''), 'cliDeny', true);
|
|
384
402
|
const ownedServers = [...new Set(ruleList(ownField(input, 'clientOwnedServers', ''), 'clientOwnedServers') ?? [])];
|
|
385
403
|
const originsRaw = ownField(input, 'origins', '');
|
|
386
404
|
let origins = {};
|
|
@@ -398,19 +416,19 @@ function readAssemblyInput(input, caps) {
|
|
|
398
416
|
const serverVersion = typeof versionRaw === 'string' ? versionRaw : undefined;
|
|
399
417
|
return { deny, ask, allow, cliDeny, ownedServers, origins, gate, serverVersion };
|
|
400
418
|
}
|
|
401
|
-
function judgeAssembly(read, caps) {
|
|
419
|
+
function judgeAssembly(read, caps, layered) {
|
|
402
420
|
const { deny, ask, allow, cliDeny, ownedServers, origins, gate, serverVersion } = read;
|
|
403
421
|
const cliSlots = slotsOf(cliDeny, 'cliDeny');
|
|
404
422
|
const cli = cliDeny === undefined ? undefined : { slots: cliSlots, fates: judgeList(cliDeny, 'deny', 'grant', serverVersion) };
|
|
405
423
|
const denySlots = slotsOf(deny, 'deny');
|
|
406
424
|
const askSlots = slotsOf(ask, 'ask');
|
|
407
425
|
const allowSlots = slotsOf(allow, 'allow');
|
|
408
|
-
const denyJ = judgeBucket(denySlots, 'deny', origins.deny, cli, undefined, 'grant', undefined, [], serverVersion);
|
|
409
|
-
const askJ = judgeBucket(askSlots, 'ask', origins.ask, undefined, undefined, 'grant', undefined, [], serverVersion);
|
|
426
|
+
const denyJ = judgeBucket(denySlots, 'deny', origins.deny, cli, undefined, 'grant', undefined, [], serverVersion, layered?.deny);
|
|
427
|
+
const askJ = judgeBucket(askSlots, 'ask', origins.ask, undefined, undefined, 'grant', undefined, [], serverVersion, layered?.ask);
|
|
410
428
|
const allowForm = allowWireForm(caps) === 'layer' ? 'layer' : 'grant';
|
|
411
429
|
const settingsGate = gate === 'settings_read_not_clean' ? 'settings_read_not_clean' : undefined;
|
|
412
430
|
let allowJ = gate === 'capability_off'
|
|
413
|
-
? { sent: [], fates: allowSlots.map(() => kept('allow_capability_off')), winnerOf: new Map(), ownedFates: ownedServers.map(() => kept('allow_capability_off')) }
|
|
431
|
+
? { sent: [], sentLayers: [], sentIds: [], fates: allowSlots.map(() => kept('allow_capability_off')), winnerOf: new Map(), ownedFates: ownedServers.map(() => kept('allow_capability_off')) }
|
|
414
432
|
: judgeBucket(allowSlots, 'allow', origins.allow, undefined, settingsGate, allowForm, undefined, ownedServers.map(clientOwnedFate));
|
|
415
433
|
if (gate !== 'capability_off' && allowForm === 'layer' && allowJ.sent.length > 0) {
|
|
416
434
|
const voiders = allowListVoiders(allowJ.sent);
|
|
@@ -426,6 +444,17 @@ function judgeAssembly(read, caps) {
|
|
|
426
444
|
buckets.ask = [...askJ.sent];
|
|
427
445
|
if (allowJ.sent.length > 0)
|
|
428
446
|
buckets.allow = [...allowJ.sent];
|
|
447
|
+
const entriesOf = (j) => j.sent.map((wire, i) => {
|
|
448
|
+
const layer = j.sentLayers[i];
|
|
449
|
+
return layer === undefined ? wire : Object.freeze({ rule: wire, layer });
|
|
450
|
+
});
|
|
451
|
+
const wireBuckets = {};
|
|
452
|
+
if (denyJ.sent.length > 0)
|
|
453
|
+
wireBuckets.deny = entriesOf(denyJ);
|
|
454
|
+
if (askJ.sent.length > 0)
|
|
455
|
+
wireBuckets.ask = entriesOf(askJ);
|
|
456
|
+
if (allowJ.sent.length > 0)
|
|
457
|
+
wireBuckets.allow = [...allowJ.sent];
|
|
429
458
|
const keptRows = [];
|
|
430
459
|
const rewritten = [];
|
|
431
460
|
const ruleRows = [];
|
|
@@ -458,21 +487,22 @@ function judgeAssembly(read, caps) {
|
|
|
458
487
|
});
|
|
459
488
|
}
|
|
460
489
|
});
|
|
461
|
-
|
|
462
|
-
const winner = j.winnerOf.get(
|
|
490
|
+
j.sent.forEach((wire, i) => {
|
|
491
|
+
const winner = j.winnerOf.get(j.sentIds[i]);
|
|
463
492
|
if (winner.at < 0)
|
|
464
|
-
|
|
493
|
+
return;
|
|
465
494
|
if (winner.rule !== wire)
|
|
466
495
|
appendOwnElement(rewritten, { from: winner.rule, to: wire, bucket });
|
|
467
|
-
}
|
|
496
|
+
});
|
|
468
497
|
}
|
|
469
|
-
return { buckets, report: { kept: keptRows, rewritten, ...(clientOwned.length > 0 ? { clientOwned } : {}) }, rows: ruleRows };
|
|
498
|
+
return { buckets, wireBuckets, report: { kept: keptRows, rewritten, ...(clientOwned.length > 0 ? { clientOwned } : {}) }, rows: ruleRows };
|
|
470
499
|
}
|
|
471
500
|
export function permissionsDeclaration(input, caps) {
|
|
472
501
|
return declarationOf(assemble(input, caps));
|
|
473
502
|
}
|
|
474
|
-
export function projectPermissionsDeclaration(input, caps, projection) {
|
|
475
|
-
|
|
503
|
+
export function projectPermissionsDeclaration(input, caps, projection, layered) {
|
|
504
|
+
const { wireBuckets } = assemble(input, caps, projection, layered);
|
|
505
|
+
return Object.keys(wireBuckets).length > 0 ? { permissions: wireBuckets } : {};
|
|
476
506
|
}
|
|
477
507
|
function declarationOf({ buckets }) {
|
|
478
508
|
return Object.keys(buckets).length > 0 ? { permissions: buckets } : {};
|