@piagent/platform 1.39.0 → 1.41.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 +26 -0
- package/README.md +1 -1
- package/README.vi.md +1 -1
- package/architecture/layers.json +10 -0
- package/docs/command-reference-vietnamese.md +2 -2
- package/docs/distribution-standard.md +6 -6
- package/docs/en/architecture.md +1 -0
- package/docs/languages.json +4 -4
- package/docs/operator-manual-vietnamese.md +7 -7
- package/docs/publishing-for-teams.md +2 -0
- package/docs/release-install-policy.md +2 -2
- package/docs/team-onboarding.md +2 -2
- package/docs/vi/architecture.md +1 -0
- package/docs-site/architecture.html +1 -1
- package/docs-site/benchmark.html +1 -1
- package/docs-site/commands.html +1 -1
- package/docs-site/content/en/index.html +4 -4
- package/docs-site/content/en/whats-new.html +12 -0
- package/docs-site/content/index.html +4 -4
- package/docs-site/content/whats-new.html +12 -0
- package/docs-site/context-engine.html +1 -1
- package/docs-site/ecosystem.html +1 -1
- package/docs-site/en/architecture.html +1 -1
- package/docs-site/en/benchmark.html +1 -1
- package/docs-site/en/commands.html +1 -1
- package/docs-site/en/context-engine.html +1 -1
- package/docs-site/en/ecosystem.html +1 -1
- package/docs-site/en/index.html +5 -5
- package/docs-site/en/mcp-approval.html +1 -1
- package/docs-site/en/mcp-auth.html +1 -1
- package/docs-site/en/mcp-servers.html +1 -1
- package/docs-site/en/mcp.html +1 -1
- package/docs-site/en/permissions.html +1 -1
- package/docs-site/en/profiles.html +1 -1
- package/docs-site/en/quickstart.html +1 -1
- package/docs-site/en/runtime.html +1 -1
- package/docs-site/en/scope.html +1 -1
- package/docs-site/en/security.html +1 -1
- package/docs-site/en/team.html +1 -1
- package/docs-site/en/whats-new.html +13 -1
- package/docs-site/en/windows.html +1 -1
- package/docs-site/en/workflows.html +1 -1
- package/docs-site/index.html +5 -5
- package/docs-site/mcp-approval.html +1 -1
- package/docs-site/mcp-auth.html +1 -1
- package/docs-site/mcp-servers.html +1 -1
- package/docs-site/mcp.html +1 -1
- package/docs-site/permissions.html +1 -1
- package/docs-site/profiles.html +1 -1
- package/docs-site/quickstart.html +1 -1
- package/docs-site/runtime.html +1 -1
- package/docs-site/scope.html +1 -1
- package/docs-site/security.html +1 -1
- package/docs-site/team.html +1 -1
- package/docs-site/whats-new.html +13 -1
- package/docs-site/windows.html +1 -1
- package/docs-site/workflows.html +1 -1
- package/evals/fixtures/piagent-webui/document-asset-v1.invalid.json +12 -0
- package/evals/fixtures/piagent-webui/document-asset-v1.valid.json +12 -0
- package/evals/private-holdout-v1/public-exposure.v2.json +2 -2
- package/package.json +1 -1
- package/packages/piagent-core/managed/request-stream.mjs +2 -2
- package/packages/piagent-core/managed/workspace-projects.mjs +61 -0
- package/packages/piagent-core/managed/workspace-report.mjs +39 -18
- package/packages/piagent-core/package.json +1 -1
- package/packages/piagent-core/runtime/inspection/document-workspace-projection.ts +78 -15
- package/packages/piagent-core/skills/fe-change-report/SKILL.md +175 -0
- package/packages/piagent-core/skills/fe-change-report/scripts/build-report.mjs +183 -0
- package/packages/piagent-core/skills/fe-change-report/scripts/capture.mjs +150 -0
- package/packages/piagent-core/skills/fe-change-report/scripts/diff-regions.mjs +110 -0
- package/packages/piagent-webui/dist/client/assets/{MarkdownMessage-CMZESBjK.js → MarkdownMessage-DeLo-xiz.js} +1 -1
- package/packages/piagent-webui/dist/client/assets/{TerminalPanel-DlcfsRaR.js → TerminalPanel-BG-DP_qm.js} +2 -2
- package/packages/piagent-webui/dist/client/assets/{TerminalView-B83HGKgY.js → TerminalView-r9rG24IO.js} +1 -1
- package/packages/piagent-webui/dist/client/assets/{WorkspaceCodeEditor-Ctf0VMQh.js → WorkspaceCodeEditor-BnqaR3pK.js} +1 -1
- package/packages/piagent-webui/dist/client/assets/{WorkspaceExplorer-fO2uXHZg.js → WorkspaceExplorer-BIxwiGtl.js} +3 -3
- package/packages/piagent-webui/dist/client/assets/{WorkspaceQuickOpen-ClaXnKl1.js → WorkspaceQuickOpen-CK3dILhQ.js} +1 -1
- package/packages/piagent-webui/dist/client/assets/index-DBUTrEXi.css +1 -0
- package/packages/piagent-webui/dist/client/assets/index-rlyRccut.js +102 -0
- package/packages/piagent-webui/dist/client/assets/{tab-close-ggR0dHlZ.js → tab-close-BoJRyBO_.js} +1 -1
- package/packages/piagent-webui/dist/client/index.html +2 -2
- package/packages/piagent-webui/extension/piagent-webui.ts +1 -0
- package/packages/piagent-webui/gateway/managed-projection.ts +4 -3
- package/packages/piagent-webui/server/core-inspection-provider.ts +5 -0
- package/packages/piagent-webui/server/ipc-read-model-client.ts +1 -0
- package/packages/piagent-webui/server/read-model-provider.ts +1 -0
- package/packages/piagent-webui/server/read-only-router.ts +6 -0
- package/packages/piagent-webui/shared/project-warning-copy.ts +15 -10
- package/schemas/piagent-webui/catalog-v1.json +9 -0
- package/schemas/piagent-webui/document-asset-v1.schema.json +57 -0
- package/schemas/piagent-webui/session-catalog-v1.schema.json +1 -1
- package/scripts/check-runtime-advisories.mjs +28 -3
- package/scripts/runtime-advisory-exceptions.json +26 -1
- package/packages/piagent-webui/dist/client/assets/index-BeBiJUDV.js +0 -102
- package/packages/piagent-webui/dist/client/assets/index-Brs_HjIo.css +0 -1
package/packages/piagent-webui/dist/client/assets/{tab-close-ggR0dHlZ.js → tab-close-BoJRyBO_.js}
RENAMED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{D as e}from"./ui-preferences-BO3AV7QE.js";import{m as t}from"./Alert-DGWhP2MM.js";import{E as n}from"./index-
|
|
1
|
+
import{D as e}from"./ui-preferences-BO3AV7QE.js";import{m as t}from"./Alert-DGWhP2MM.js";import{E as n}from"./index-rlyRccut.js";var r=e(),i=t((0,r.jsx)(`path`,{d:`M19 6.41 17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z`}),`Close`);function a(e,t,a){let o=e=>{e.stopPropagation(),e.preventDefault(),a()};return(0,r.jsxs)(n,{component:`span`,sx:{display:`inline-flex`,alignItems:`center`,gap:.75},children:[(0,r.jsx)(`span`,{children:e}),(0,r.jsx)(n,{component:`span`,role:`button`,tabIndex:0,"aria-label":t,title:t,onClick:o,onMouseDown:e=>e.stopPropagation(),onKeyDown:e=>{(e.key===`Enter`||e.key===` `)&&o(e)},sx:{display:`inline-grid`,placeItems:`center`,width:18,height:18,borderRadius:`2px`,opacity:.7,"&:hover":{opacity:1,bgcolor:`action.hover`}},children:(0,r.jsx)(i,{sx:{fontSize:14}})})]})}export{i as n,a as t};
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
<meta name="csp-nonce" content="__PIAGENT_CSP_NONCE__" />
|
|
8
8
|
<meta name="piagent-webui-mode" content="__PIAGENT_WEBUI_MODE__" />
|
|
9
9
|
<title>Piagent WebUI</title>
|
|
10
|
-
<script type="module" crossorigin src="/assets/index-
|
|
10
|
+
<script type="module" crossorigin src="/assets/index-rlyRccut.js"></script>
|
|
11
11
|
<link rel="modulepreload" crossorigin href="/assets/rolldown-runtime-hePW80VL.js">
|
|
12
12
|
<link rel="modulepreload" crossorigin href="/assets/ui-preferences-BO3AV7QE.js">
|
|
13
13
|
<link rel="modulepreload" crossorigin href="/assets/useId-DEefKUAT.js">
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
<link rel="modulepreload" crossorigin href="/assets/DialogTitle-CyaFPaiW.js">
|
|
17
17
|
<link rel="modulepreload" crossorigin href="/assets/Button-QItfZUNj.js">
|
|
18
18
|
<link rel="modulepreload" crossorigin href="/assets/bootstrap-CedkNtRG.js">
|
|
19
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
19
|
+
<link rel="stylesheet" crossorigin href="/assets/index-DBUTrEXi.css">
|
|
20
20
|
</head>
|
|
21
21
|
<body>
|
|
22
22
|
<div id="root"></div>
|
|
@@ -155,6 +155,7 @@ function readMethod(provider: CoreInspectionProvider, queue: HeldMessageQueue, s
|
|
|
155
155
|
if (method === "releaseMonitor" && args.length === 0) return provider.releaseMonitor();
|
|
156
156
|
if (method === "documents" && args.length === 0) return provider.documents();
|
|
157
157
|
if (method === "document" && args.length === 1 && typeof args[0] === "string" && REF.test(args[0])) return provider.document(args[0]);
|
|
158
|
+
if (method === "documentAsset" && args.length === 2 && typeof args[0] === "string" && REF.test(args[0]) && typeof args[1] === "string" && REF.test(args[1])) return provider.documentAsset(args[0], args[1]);
|
|
158
159
|
if (method === "logPreview" && args.length === 1 && typeof args[0] === "string" && REF.test(args[0])) return provider.logPreview(args[0]);
|
|
159
160
|
if (method === "transcript" && args.length === 2 && (args[0] === null || typeof args[0] === "string" && REF.test(args[0]))
|
|
160
161
|
&& Number.isInteger(args[1]) && Number(args[1]) >= 1 && Number(args[1]) <= 200) return provider.transcript(args[0] as string | null, Number(args[1]));
|
|
@@ -36,10 +36,11 @@ export function managedProjection(context: any, entries: any[]) {
|
|
|
36
36
|
const permission = chosen === 'bypass' ? (bypassAllowed ? 'bypass' : 'trusted-full-access') : chosen === 'trusted-full-access' ? 'trusted-full-access' : 'workspace-write';
|
|
37
37
|
// Fast mode, only where the conversation's Harness allows it (fast-mode.mjs).
|
|
38
38
|
const fast = last('agent-watch-fast');
|
|
39
|
-
//
|
|
40
|
-
|
|
39
|
+
// Projects Studio says do not belong to the key's team (workspace-report.mjs);
|
|
40
|
+
// a local project carries its folder name.
|
|
41
|
+
const project = last('agent-watch-project'), kinds = ['other_team', 'unlisted', 'unregistered', 'no_repository'];
|
|
41
42
|
const projectWarnings = Array.isArray(project?.warnings) ? project.warnings.filter((w: any) => kinds.includes(w?.kind) && typeof w.repo === 'string' && w.repo.trim())
|
|
42
|
-
.slice(0, 6).map((w: any) => ({ repo: w.repo.trim().slice(0, 200), kind: w.kind })) : [];
|
|
43
|
+
.slice(0, 6).map((w: any) => ({ repo: w.repo.trim().slice(0, 200), kind: w.kind, ...(typeof w.name === 'string' && w.name.trim() ? { name: w.name.trim().slice(0, 80) } : {}) })) : [];
|
|
43
44
|
const projectWarning = projectWarnings.length ? { warnings: projectWarnings, ...(typeof project.team === 'string' && project.team.trim() ? { team: project.team.trim().slice(0, 80) } : {}) } : null;
|
|
44
45
|
return {modelLabel:'agent-watch-auto', managedPermission: permission, ...(bypassAllowed ? {managedBypass: true as const} : {}), ...(fast?.allowed === true ? {managedFast: fast.enabled === true ? 'on' as const : 'off' as const} : {}), managedHelpers:{active:Number.isInteger(counter?.active) && counter.active>=0 && counter.active<=maximum ? counter.active : 0, maximum},
|
|
45
46
|
...(Array.isArray(capabilities)?{managedThinkingLevels:levels.filter(level=>capabilities.includes(level))}:{}),
|
|
@@ -246,6 +246,11 @@ export class CoreInspectionProvider implements WebUiReadModelProvider {
|
|
|
246
246
|
return core.projectDocumentWorkspaceDocument({ ...this.#documentWorkspaceInput(core.matchesProtectedPath), documentRef });
|
|
247
247
|
}
|
|
248
248
|
|
|
249
|
+
async documentAsset(documentRef: string, name: string): Promise<unknown> {
|
|
250
|
+
const core = await loadCoreInspection();
|
|
251
|
+
return core.projectDocumentWorkspaceAsset({ ...this.#documentWorkspaceInput(core.matchesProtectedPath), documentRef, name });
|
|
252
|
+
}
|
|
253
|
+
|
|
249
254
|
#documentWorkspaceInput(matchesProtectedPath: (candidate: string, patterns: string[]) => unknown) {
|
|
250
255
|
const protectedPaths = this.#input.protectedPaths?.() ?? [];
|
|
251
256
|
return { cwd: this.#input.cwd, profileRoots: this.#input.documentReadRoots?.(),
|
|
@@ -64,6 +64,7 @@ export class IpcReadModelClient implements WebUiReadModelProvider {
|
|
|
64
64
|
releaseMonitor(): Promise<unknown> { return this.#request("releaseMonitor"); }
|
|
65
65
|
documents(): Promise<unknown> { return this.#request("documents"); }
|
|
66
66
|
document(documentRef: string): Promise<unknown> { return this.#request("document", [documentRef]); }
|
|
67
|
+
documentAsset(documentRef: string, name: string): Promise<unknown> { return this.#request("documentAsset", [documentRef, name]); }
|
|
67
68
|
activity(): Promise<unknown> { return this.#request("activity"); }
|
|
68
69
|
logPreview(activityRef: string): Promise<unknown> { return this.#request("logPreview", [activityRef]); }
|
|
69
70
|
transcript(beforeCursor: string | null, limit: number): Promise<unknown> { return this.#request("transcript", [beforeCursor, limit]); }
|
|
@@ -27,6 +27,7 @@ export type WebUiReadModelProvider = {
|
|
|
27
27
|
releaseMonitor(): unknown | Promise<unknown>;
|
|
28
28
|
documents(): unknown | Promise<unknown>;
|
|
29
29
|
document(documentRef: string): unknown | Promise<unknown>;
|
|
30
|
+
documentAsset?(documentRef: string, name: string): unknown | Promise<unknown>;
|
|
30
31
|
activity(): unknown | Promise<unknown>;
|
|
31
32
|
logPreview(activityRef: string): unknown | Promise<unknown>;
|
|
32
33
|
transcript(beforeCursor: string | null, limit: number): unknown | Promise<unknown>;
|
|
@@ -33,6 +33,12 @@ export async function routeReadOnlyRequest(url: URL, provider: WebUiReadModelPro
|
|
|
33
33
|
return { handled: true, status: 200, value: await provider.subagentTree(ref) };
|
|
34
34
|
}
|
|
35
35
|
if (url.pathname === "/api/v1/documents" && !url.search) return { handled: true, status: 200, value: await provider.documents() };
|
|
36
|
+
const asset = /^\/api\/v1\/documents\/([^/]+)\/assets\/([^/]+)$/.exec(url.pathname);
|
|
37
|
+
if (asset) {
|
|
38
|
+
if (url.search || !OPAQUE_REF.test(asset[1]) || !OPAQUE_REF.test(asset[2])) return { handled: true, status: 400, value: { error: { code: "invalid-document-asset-ref" } } };
|
|
39
|
+
if (!provider.documentAsset) return { handled: true, status: 503, value: { error: { code: "document-asset-unavailable" } } };
|
|
40
|
+
return { handled: true, status: 200, value: await provider.documentAsset(asset[1], asset[2]) };
|
|
41
|
+
}
|
|
36
42
|
if (url.pathname.startsWith("/api/v1/documents/")) {
|
|
37
43
|
const ref = url.pathname.slice("/api/v1/documents/".length);
|
|
38
44
|
if (url.search || !OPAQUE_REF.test(ref) || ref.includes("/")) return { handled: true, status: 400, value: { error: { code: "invalid-document-ref" } } };
|
|
@@ -1,16 +1,21 @@
|
|
|
1
1
|
// The line a member reads when Studio says their company conversation works
|
|
2
|
-
// in a
|
|
3
|
-
//
|
|
2
|
+
// in a project that does not belong to the key's team ("agent-watch-project"),
|
|
3
|
+
// in the dashboard and in the company Terminal. A local project (no readable
|
|
4
|
+
// remote) is named by its folder.
|
|
4
5
|
import type { CopyLocale } from "./company-copy.ts";
|
|
5
6
|
|
|
6
|
-
export type ProjectWarningItem = { repo: string; kind: "other_team" | "unlisted" };
|
|
7
|
+
export type ProjectWarningItem = { repo: string; kind: "other_team" | "unlisted" | "unregistered" | "no_repository"; name?: string };
|
|
8
|
+
|
|
9
|
+
const label = (item: ProjectWarningItem) => item.name ?? item.repo;
|
|
7
10
|
|
|
8
11
|
export function projectWarningText(warnings: readonly ProjectWarningItem[], team: string | null | undefined, locale: CopyLocale): string {
|
|
9
|
-
const
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
12
|
+
const vi = locale === "vi", name = team ? ` ${team}` : "", told = vi ? " Admin Studio đã được báo." : " Studio administrators were told.";
|
|
13
|
+
const of = (kind: ProjectWarningItem["kind"]) => warnings.filter(item => item.kind === kind).map(label).join(", ");
|
|
14
|
+
const parts: string[] = [];
|
|
15
|
+
const other = of("other_team"), unlisted = of("unlisted"), unregistered = of("unregistered"), none = of("no_repository");
|
|
16
|
+
if (other) parts.push(vi ? `${other} thuộc team khác, không phải team${name} của key này.` : `${other} belongs to another team, not this key's team${name}.`);
|
|
17
|
+
if (unlisted) parts.push(vi ? `${unlisted} chưa có trong danh sách project của team${name}.` : `${unlisted} is not in the project list of team${name}.`);
|
|
18
|
+
if (unregistered) parts.push(vi ? `${unregistered} chưa được đăng ký cho team${name}.` : `${unregistered} is not registered to team${name}.`);
|
|
19
|
+
if (none) parts.push(vi ? `Không xác định được project của thư mục ${none}.` : `The project of folder ${none} could not be identified.`);
|
|
20
|
+
return parts.join(" ") + told;
|
|
16
21
|
}
|
|
@@ -182,6 +182,15 @@
|
|
|
182
182
|
"supported": true,
|
|
183
183
|
"current": true
|
|
184
184
|
},
|
|
185
|
+
{
|
|
186
|
+
"name": "document-asset-v1",
|
|
187
|
+
"file": "document-asset-v1.schema.json",
|
|
188
|
+
"id": "https://piagent.io.vn/schemas/piagent-webui/document-asset-v1.schema.json",
|
|
189
|
+
"documentVersion": 1,
|
|
190
|
+
"role": "read-model",
|
|
191
|
+
"supported": true,
|
|
192
|
+
"current": true
|
|
193
|
+
},
|
|
185
194
|
{
|
|
186
195
|
"name": "control-command-v1",
|
|
187
196
|
"file": "control-command-v1.schema.json",
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://piagent.io.vn/schemas/piagent-webui/document-asset-v1.schema.json",
|
|
4
|
+
"title": "Piagent WebUI change report screenshot v1",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"additionalProperties": false,
|
|
7
|
+
"required": ["schemaVersion", "version", "generatedAt", "documentRef", "name", "state", "mediaType", "sizeBytes", "base64", "reasonCode"],
|
|
8
|
+
"properties": {
|
|
9
|
+
"schemaVersion": { "const": 1 },
|
|
10
|
+
"version": { "const": "piagent-webui-document-asset-v1" },
|
|
11
|
+
"generatedAt": { "$ref": "common-v1.schema.json#/$defs/timestamp" },
|
|
12
|
+
"documentRef": { "$ref": "common-v1.schema.json#/$defs/opaqueRef" },
|
|
13
|
+
"name": {
|
|
14
|
+
"anyOf": [
|
|
15
|
+
{ "type": "string", "minLength": 1, "maxLength": 128, "pattern": "^(before|after|diff)-[a-z0-9][a-z0-9-]{0,100}\\.png$" },
|
|
16
|
+
{ "type": "null" }
|
|
17
|
+
]
|
|
18
|
+
},
|
|
19
|
+
"state": { "enum": ["ready", "unavailable"] },
|
|
20
|
+
"mediaType": { "enum": ["image/png", null] },
|
|
21
|
+
"sizeBytes": {
|
|
22
|
+
"anyOf": [
|
|
23
|
+
{ "type": "integer", "minimum": 8, "maximum": 8388608 },
|
|
24
|
+
{ "type": "null" }
|
|
25
|
+
]
|
|
26
|
+
},
|
|
27
|
+
"base64": {
|
|
28
|
+
"anyOf": [
|
|
29
|
+
{ "type": "string", "minLength": 12, "maxLength": 11184812, "pattern": "^[A-Za-z0-9+/]+={0,2}$" },
|
|
30
|
+
{ "type": "null" }
|
|
31
|
+
]
|
|
32
|
+
},
|
|
33
|
+
"reasonCode": { "$ref": "common-v1.schema.json#/$defs/nullableReasonCode" }
|
|
34
|
+
},
|
|
35
|
+
"allOf": [
|
|
36
|
+
{
|
|
37
|
+
"if": { "properties": { "state": { "const": "ready" } }, "required": ["state"] },
|
|
38
|
+
"then": {
|
|
39
|
+
"properties": {
|
|
40
|
+
"name": { "type": "string" },
|
|
41
|
+
"mediaType": { "const": "image/png" },
|
|
42
|
+
"sizeBytes": { "type": "integer" },
|
|
43
|
+
"base64": { "type": "string" },
|
|
44
|
+
"reasonCode": { "type": "null" }
|
|
45
|
+
}
|
|
46
|
+
},
|
|
47
|
+
"else": {
|
|
48
|
+
"properties": {
|
|
49
|
+
"mediaType": { "type": "null" },
|
|
50
|
+
"sizeBytes": { "type": "null" },
|
|
51
|
+
"base64": { "type": "null" },
|
|
52
|
+
"reasonCode": { "$ref": "common-v1.schema.json#/$defs/reasonCode" }
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
]
|
|
57
|
+
}
|
|
@@ -92,7 +92,7 @@
|
|
|
92
92
|
"composerAvailable": { "type": "boolean" },
|
|
93
93
|
"needsAttention": { "type": "boolean" },
|
|
94
94
|
"managedHelpers": { "type": "object", "additionalProperties": false, "required": ["active", "maximum"], "properties": {"active": {"type": ["integer", "null"], "minimum": 0, "maximum": 4}, "maximum": {"type": "integer", "minimum": 0, "maximum": 4}} },
|
|
95
|
-
"managedThinkingLevels": {"type": "array", "uniqueItems": true, "maxItems": 7, "items": {"enum": ["off","minimal","low","medium","high","xhigh","max"]}}, "managedPermission": {"enum": ["workspace-write","trusted-full-access","bypass"]}, "managedBypass": {"const": true}, "managedProjectWarning": {"type": "object", "additionalProperties": false, "required": ["warnings"], "properties": {"team": {"type": "string", "minLength": 1, "maxLength": 80}, "warnings": {"type": "array", "minItems": 1, "maxItems": 6, "items": {"type": "object", "additionalProperties": false, "required": ["repo", "kind"], "properties": {"repo": {"type": "string", "minLength": 1, "maxLength": 200}, "kind": {"enum": ["other_team", "unlisted"]}}}}}}, "managedFast": {"enum": ["off","on"]},
|
|
95
|
+
"managedThinkingLevels": {"type": "array", "uniqueItems": true, "maxItems": 7, "items": {"enum": ["off","minimal","low","medium","high","xhigh","max"]}}, "managedPermission": {"enum": ["workspace-write","trusted-full-access","bypass"]}, "managedBypass": {"const": true}, "managedProjectWarning": {"type": "object", "additionalProperties": false, "required": ["warnings"], "properties": {"team": {"type": "string", "minLength": 1, "maxLength": 80}, "warnings": {"type": "array", "minItems": 1, "maxItems": 6, "items": {"type": "object", "additionalProperties": false, "required": ["repo", "kind"], "properties": {"repo": {"type": "string", "minLength": 1, "maxLength": 200}, "name": {"type": "string", "minLength": 1, "maxLength": 80}, "kind": {"enum": ["other_team", "unlisted", "unregistered", "no_repository"]}}}}}}, "managedFast": {"enum": ["off","on"]},
|
|
96
96
|
"managedPlan": { "type": "object", "additionalProperties": false, "required": ["steps"], "properties": {
|
|
97
97
|
"steps": { "type": "array", "minItems": 1, "maxItems": 30, "items": { "type": "object", "additionalProperties": false, "required": ["step", "status"], "properties": {
|
|
98
98
|
"step": { "type": "string", "minLength": 1, "maxLength": 200 }, "status": { "enum": ["pending", "in_progress", "completed", "deferred"] } } } } } },
|
|
@@ -7,7 +7,11 @@
|
|
|
7
7
|
// The only exceptions are listed in runtime-advisory-exceptions.json, each an
|
|
8
8
|
// owner decision for one advisory at one package path in the tree, with a day
|
|
9
9
|
// it stops applying. The same advisory anywhere else, any other advisory, and
|
|
10
|
-
// an exception past its day all block. Its tests check every entry.
|
|
10
|
+
// an exception past its day all block. Its tests check every entry. A package
|
|
11
|
+
// flagged only because it depends on flagged packages (no advisory of its
|
|
12
|
+
// own, `via` names packages) is carried: it passes when every package it
|
|
13
|
+
// names is in the report with all of its advisories accepted, and blocks
|
|
14
|
+
// otherwise.
|
|
11
15
|
import fs from "node:fs";
|
|
12
16
|
|
|
13
17
|
function readStdin() {
|
|
@@ -50,12 +54,14 @@ const { exceptions } = JSON.parse(fs.readFileSync(process.env.PIAGENT_ADVISORY_E
|
|
|
50
54
|
// A test seam for the day exceptions are judged on; the gate uses today (UTC).
|
|
51
55
|
const today = process.env.PIAGENT_ADVISORY_TODAY ?? new Date().toISOString().slice(0, 10);
|
|
52
56
|
|
|
53
|
-
const blocking = [], accepted = [];
|
|
57
|
+
const blocking = [], accepted = [], pending = [], carried = [];
|
|
54
58
|
for (const [name, vulnerability] of Object.entries(report.vulnerabilities ?? {})) {
|
|
55
59
|
if (!["moderate", "high", "critical"].includes(vulnerability.severity)) continue;
|
|
56
60
|
const ids = advisoryIdsFor(vulnerability);
|
|
57
61
|
if (ids.length === 0) {
|
|
58
|
-
|
|
62
|
+
const via = vulnerability.via ?? [];
|
|
63
|
+
if (via.length > 0 && via.every((item) => typeof item === "string")) pending.push({ name, severity: vulnerability.severity, via });
|
|
64
|
+
else blocking.push({ name, severity: vulnerability.severity, id: "(no advisory id)" });
|
|
59
65
|
continue;
|
|
60
66
|
}
|
|
61
67
|
const nodes = Array.isArray(vulnerability.nodes) ? vulnerability.nodes : [];
|
|
@@ -67,10 +73,29 @@ for (const [name, vulnerability] of Object.entries(report.vulnerabilities ?? {})
|
|
|
67
73
|
}
|
|
68
74
|
}
|
|
69
75
|
|
|
76
|
+
// Carried packages may chain (a via b via c): settle them until nothing changes;
|
|
77
|
+
// whatever is left names a package that is not accepted.
|
|
78
|
+
const cleared = new Set(accepted.map((entry) => entry.name).filter((name) => !blocking.some((entry) => entry.name === name)));
|
|
79
|
+
for (let changed = true; changed;) {
|
|
80
|
+
changed = false;
|
|
81
|
+
for (const entry of [...pending]) {
|
|
82
|
+
if (!entry.via.every((name) => cleared.has(name))) continue;
|
|
83
|
+
pending.splice(pending.indexOf(entry), 1);
|
|
84
|
+
carried.push(entry);
|
|
85
|
+
cleared.add(entry.name);
|
|
86
|
+
changed = true;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
for (const entry of pending) blocking.push({ name: entry.name, severity: entry.severity, id: `(no advisory id, through ${entry.via.join(", ")})` });
|
|
90
|
+
|
|
70
91
|
for (const entry of accepted) {
|
|
71
92
|
console.log(`ACCEPTED: ${entry.severity} advisory ${entry.id} in ${entry.name} at ${entry.exception.node} until ${entry.exception.until} (owner decision ${entry.exception.decided})`);
|
|
72
93
|
}
|
|
73
94
|
|
|
95
|
+
for (const entry of carried) {
|
|
96
|
+
console.log(`CARRIED: ${entry.severity} in ${entry.name} only through accepted advisories of ${entry.via.join(", ")}`);
|
|
97
|
+
}
|
|
98
|
+
|
|
74
99
|
const failures = blocking.map(
|
|
75
100
|
(entry) => `${entry.severity} advisory ${entry.id} in ${entry.name} blocks the supported runtime`
|
|
76
101
|
+ (entry.expired ? ` (its exception ended ${entry.expired})` : "")
|
|
@@ -1,4 +1,29 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 1,
|
|
3
|
-
"exceptions": [
|
|
3
|
+
"exceptions": [
|
|
4
|
+
{
|
|
5
|
+
"id": "GHSA-xjj6-f33q-929c",
|
|
6
|
+
"package": "undici",
|
|
7
|
+
"node": "node_modules/undici",
|
|
8
|
+
"decided": "2026-10-24",
|
|
9
|
+
"until": "2026-11-07",
|
|
10
|
+
"reason": "Denial of service (an unhandled error crashes the process) when a response lists too many content-encodings; no data exposure. undici 8.10.2 is an exact dependency of every published Pi host (1.3.0 and 1.4.0), installed globally, so no Piagent change can update it; 8.12.2 fixes it. Remove when a Pi host carries undici >= 8.12.2."
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
"id": "GHSA-4qv8-2p9j-2685",
|
|
14
|
+
"package": "undici",
|
|
15
|
+
"node": "node_modules/undici",
|
|
16
|
+
"decided": "2026-10-24",
|
|
17
|
+
"until": "2026-11-07",
|
|
18
|
+
"reason": "Denial of service (unbounded memory) in undici's EventSource parser fed by a hostile server; no data exposure. Pinned by the Pi host as above; fixed in undici 8.12.2."
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
"id": "GHSA-f72v-6w45-q472",
|
|
22
|
+
"package": "undici",
|
|
23
|
+
"node": "node_modules/undici",
|
|
24
|
+
"decided": "2026-10-24",
|
|
25
|
+
"until": "2026-11-07",
|
|
26
|
+
"reason": "Subresource Integrity check bypassed with a non-lowercase algorithm token; Pi does not fetch with integrity metadata. Pinned by the Pi host as above; fixed in undici 8.12.2."
|
|
27
|
+
}
|
|
28
|
+
]
|
|
4
29
|
}
|