@piagent/platform 1.16.2 → 1.17.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 +16 -0
- package/README.md +1 -1
- package/README.vi.md +1 -1
- package/docs/command-reference-vietnamese.md +2 -2
- package/docs/distribution-standard.md +6 -6
- package/docs/languages.json +2 -2
- 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-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/private-holdout-v1/public-exposure.v2.json +2 -2
- package/package.json +1 -1
- package/packages/piagent-core/package.json +1 -1
- package/packages/piagent-webui/dist/client/assets/{MarkdownMessage-BDdLJYnZ.js → MarkdownMessage-CeQ79h30.js} +1 -1
- package/packages/piagent-webui/dist/client/assets/{index-DJGByyfL.js → index-BNQK7rHr.js} +11 -11
- package/packages/piagent-webui/dist/client/index.html +1 -1
- package/packages/piagent-webui/gateway/git-head.ts +52 -0
- package/packages/piagent-webui/gateway/session-catalog.ts +8 -2
- package/schemas/piagent-webui/session-catalog-v1.schema.json +3 -0
- package/scripts/dashboard-update-job.mjs +4 -1
- package/scripts/piagent-dashboard.mjs +13 -1
|
@@ -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-BNQK7rHr.js"></script>
|
|
11
11
|
<link rel="stylesheet" crossorigin href="/assets/index-Z91z6SlN.css">
|
|
12
12
|
</head>
|
|
13
13
|
<body>
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
|
|
4
|
+
export type GitHead = { name: string; detached: boolean };
|
|
5
|
+
|
|
6
|
+
const MAX_DEPTH = 40;
|
|
7
|
+
|
|
8
|
+
// The branch a project folder stands on, read from the repository's HEAD file
|
|
9
|
+
// rather than by running git: the catalog asks for every project each time the
|
|
10
|
+
// dashboard refreshes, and a file read costs nothing. A linked worktree's
|
|
11
|
+
// `.git` is a file naming its own git directory, which holds its own HEAD.
|
|
12
|
+
// Null when the folder is not inside a repository or HEAD cannot be read.
|
|
13
|
+
export function readGitHead(cwd: string): GitHead | null {
|
|
14
|
+
if (!cwd) return null;
|
|
15
|
+
let folder = path.resolve(cwd);
|
|
16
|
+
for (let depth = 0; depth < MAX_DEPTH; depth += 1) {
|
|
17
|
+
const dotGit = path.join(folder, ".git");
|
|
18
|
+
let stat: fs.Stats | null = null;
|
|
19
|
+
try { stat = fs.statSync(dotGit); } catch { /* keep walking up */ }
|
|
20
|
+
if (stat) return headAt(stat.isDirectory() ? dotGit : linkedGitDir(dotGit, folder));
|
|
21
|
+
const parent = path.dirname(folder);
|
|
22
|
+
if (parent === folder) return null;
|
|
23
|
+
folder = parent;
|
|
24
|
+
}
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function linkedGitDir(dotGitFile: string, folder: string): string | null {
|
|
29
|
+
try {
|
|
30
|
+
const match = /^gitdir:\s*(.+?)\s*$/m.exec(fs.readFileSync(dotGitFile, "utf8").slice(0, 4096));
|
|
31
|
+
return match ? path.resolve(folder, match[1]) : null;
|
|
32
|
+
} catch { return null; }
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function headAt(gitDir: string | null): GitHead | null {
|
|
36
|
+
if (!gitDir) return null;
|
|
37
|
+
let head: string;
|
|
38
|
+
try { head = fs.readFileSync(path.join(gitDir, "HEAD"), "utf8").slice(0, 1024).trim(); } catch { return null; }
|
|
39
|
+
const ref = /^ref:\s*refs\/heads\/(.+)$/.exec(head);
|
|
40
|
+
if (ref) return { name: ref[1], detached: false };
|
|
41
|
+
if (/^[0-9a-f]{40,64}$/i.test(head)) return { name: head.slice(0, 7), detached: true };
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// One read per project folder while a catalog is built.
|
|
46
|
+
export function gitHeadReader(): (cwd: string) => GitHead | null {
|
|
47
|
+
const seen = new Map<string, GitHead | null>();
|
|
48
|
+
return (cwd) => {
|
|
49
|
+
if (!seen.has(cwd)) seen.set(cwd, readGitHead(cwd));
|
|
50
|
+
return seen.get(cwd)!;
|
|
51
|
+
};
|
|
52
|
+
}
|
|
@@ -6,6 +6,7 @@ import { workflowCommandPattern } from "../../piagent-core/runtime/workflows/web
|
|
|
6
6
|
import type { Catalog, SessionRow } from "../contracts/generated/session-catalog-v1.ts";
|
|
7
7
|
import type { MetadataSnapshot, SessionMetadata } from "./session-metadata-store.ts";
|
|
8
8
|
import { projectRefForCwd, sessionRefForPath } from "../ownership/session-refs.ts";
|
|
9
|
+
import { gitHeadReader, type GitHead } from "./git-head.ts";
|
|
9
10
|
|
|
10
11
|
export { projectRefForCwd, sessionRefForPath } from "../ownership/session-refs.ts";
|
|
11
12
|
|
|
@@ -105,7 +106,8 @@ function revision(key: Buffer, value: unknown): string {
|
|
|
105
106
|
}
|
|
106
107
|
|
|
107
108
|
function row(key: Buffer, info: PiSessionInfo, metadata: SessionMetadata | undefined, metadataReason: string | null,
|
|
108
|
-
ownership?: SessionOwnerProjection, sessionOptions?: SessionOptionProjection): SessionRow {
|
|
109
|
+
ownership?: SessionOwnerProjection, sessionOptions?: SessionOptionProjection, gitHead?: GitHead | null): SessionRow {
|
|
110
|
+
const gitBranch = gitHead ? { name: display(gitHead.name, 200, "HEAD"), detached: gitHead.detached } : null;
|
|
109
111
|
const projectLabel = display(info.cwd ? path.basename(info.cwd) : "", 120, "Unknown project");
|
|
110
112
|
const projected = projectedSessionTitle(info);
|
|
111
113
|
return {
|
|
@@ -123,6 +125,7 @@ function row(key: Buffer, info: PiSessionInfo, metadata: SessionMetadata | undef
|
|
|
123
125
|
unread: metadata?.unread ?? false,
|
|
124
126
|
composerAvailable: metadata?.archived ? false : ownership?.composerAvailable ?? false,
|
|
125
127
|
needsAttention: metadata?.archived ? false : ownership?.needsAttention ?? false,
|
|
128
|
+
...(gitBranch ? { gitBranch } : {}),
|
|
126
129
|
...(sessionOptions?.managedHelpers ? {managedHelpers:{...sessionOptions.managedHelpers,
|
|
127
130
|
active: ownership?.liveState === 'idle' ? 0 : ownership?.liveState === 'running' || ownership?.liveState === 'waiting-approval' ? sessionOptions.managedHelpers.active : null}} : {}),
|
|
128
131
|
...(sessionOptions?.managedThinkingLevels ? {managedThinkingLevels:sessionOptions.managedThinkingLevels} : {}),
|
|
@@ -138,6 +141,7 @@ function row(key: Buffer, info: PiSessionInfo, metadata: SessionMetadata | undef
|
|
|
138
141
|
: ownership?.owner ?? { kind: "none", ownerEpoch: null, gatewayInstanceRef: null, runtimeInstanceRef: null, continuity: "unknown" },
|
|
139
142
|
sessionRevision: revision(key, [info.path, info.modified.toISOString(), info.messageCount, info.name ?? null, metadata?.revision ?? null,
|
|
140
143
|
sessionOptions?.modelLabel ?? null, sessionOptions?.thinkingLevel ?? "unknown", sessionOptions?.managedHelpers, sessionOptions?.managedThinkingLevels, sessionOptions?.managedPermission ?? null, sessionOptions?.managedPlan, sessionOptions?.managedProcess,
|
|
144
|
+
gitBranch,
|
|
141
145
|
metadata?.archived ? "archived" : ownership ? [ownership.state, ownership.liveState, ownership.owner.kind,
|
|
142
146
|
ownership.owner.ownerEpoch, ownership.owner.runtimeInstanceRef, ownership.reasonCode] : "offline"]),
|
|
143
147
|
reasonCode: metadata?.archived ? metadataReason : ownership?.reasonCode ?? metadataReason
|
|
@@ -151,16 +155,18 @@ export async function buildSessionCatalog(options: {
|
|
|
151
155
|
readMetadata?(): MetadataSnapshot;
|
|
152
156
|
readOwnership?(sessionRef: string): SessionOwnerProjection;
|
|
153
157
|
readSessionOptions?(info: PiSessionInfo): SessionOptionProjection;
|
|
158
|
+
readGitHead?(cwd: string): GitHead | null;
|
|
154
159
|
limit?: number;
|
|
155
160
|
}): Promise<Catalog> {
|
|
156
161
|
const limit = Math.max(1, Math.min(200, options.limit ?? 200));
|
|
157
162
|
try {
|
|
158
163
|
const found = (await options.listSessions()).filter(isUserConversationSession);
|
|
164
|
+
const readHead = options.readGitHead ?? gitHeadReader();
|
|
159
165
|
const metadata = options.readMetadata?.() ?? { state: "ready", revision: null, sessions: new Map(), reasonCode: null };
|
|
160
166
|
const sessions = found.map((info) => {
|
|
161
167
|
const sessionRef = sessionRefForPath(options.key, info.path);
|
|
162
168
|
return row(options.key, info, metadata.sessions.get(sessionRef), metadata.reasonCode, options.readOwnership?.(sessionRef),
|
|
163
|
-
options.readSessionOptions?.(info));
|
|
169
|
+
options.readSessionOptions?.(info), readHead(info.cwd));
|
|
164
170
|
}).sort((left, right) => Number(right.pinned) - Number(left.pinned)
|
|
165
171
|
|| Date.parse(right.updatedAt) - Date.parse(left.updatedAt)).slice(0, limit);
|
|
166
172
|
return {
|
|
@@ -99,6 +99,9 @@
|
|
|
99
99
|
"managedProcess": { "type": "object", "additionalProperties": false, "required": ["outcome", "verified", "reviewed", "blockingOpen"], "properties": {
|
|
100
100
|
"outcome": { "enum": ["no_change", "interrupted", "disputed", "blocking_open", "unverified", "review_unavailable", "unreviewed", "clean"] },
|
|
101
101
|
"verified": { "type": "boolean" }, "reviewed": { "type": "boolean" }, "blockingOpen": { "type": "integer", "minimum": 0, "maximum": 1000 }, "planSkipped": { "const": true } } },
|
|
102
|
+
"gitBranch": { "type": "object", "additionalProperties": false, "required": ["name", "detached"], "properties": {
|
|
103
|
+
"name": { "type": "string", "minLength": 1, "maxLength": 200, "pattern": "^[^\\u0000-\\u001F\\u007F]*$" },
|
|
104
|
+
"detached": { "type": "boolean" } } },
|
|
102
105
|
"modelLabel": { "$ref": "common-v1.schema.json#/$defs/nullableDisplayText" },
|
|
103
106
|
"thinkingLevel": { "enum": ["off", "minimal", "low", "medium", "high", "xhigh", "max", "unknown"] },
|
|
104
107
|
"contextUsage": { "$ref": "#/$defs/contextUsage" },
|
|
@@ -72,6 +72,9 @@ fs.writeSync(log, "\nRestarting the dashboard on the new release\n");
|
|
|
72
72
|
// install sits under node_modules, where Node does not strip types itself.
|
|
73
73
|
// --import takes a URL: a Windows path (C:\...) reads as a URL scheme.
|
|
74
74
|
const loader = pathToFileURL(path.join(packageRoot, "scripts", "register-typescript-loader.mjs")).href;
|
|
75
|
+
// The new dashboard checks for the next release itself: only the update step
|
|
76
|
+
// runs without the check.
|
|
77
|
+
const restartEnv = { ...process.env }; delete restartEnv.PIAGENT_NO_UPDATE_CHECK;
|
|
75
78
|
const restart = spawnSync(process.execPath, ["--disable-warning=ExperimentalWarning", "--import", loader, dashboard, "restart", "--json"],
|
|
76
|
-
{ stdio: ["ignore", "ignore", log], env, timeout: 3 * 60 * 1000 });
|
|
79
|
+
{ stdio: ["ignore", "ignore", log], env: restartEnv, timeout: 3 * 60 * 1000 });
|
|
77
80
|
finish("succeeded", { installed, ...(bindingChanged ? { bindingChanged } : {}), ...(restart.status === 0 ? {} : { reason: "dashboard-restart-failed" }) });
|
|
@@ -154,6 +154,18 @@ async function waitForGatewayStopped(previous, timeoutMs = 8_000) {
|
|
|
154
154
|
throw new Error("gateway-restart-stop-timeout\nThe previous Gateway did not stop; restart refused to reuse the old process.");
|
|
155
155
|
}
|
|
156
156
|
|
|
157
|
+
// An update job of an earlier release restarted the dashboard with update
|
|
158
|
+
// checks switched off (it meant them for its own install step only): a
|
|
159
|
+
// dashboard it starts checks for releases again.
|
|
160
|
+
function serveEnvironment(home = os.homedir()) {
|
|
161
|
+
const env = { ...process.env };
|
|
162
|
+
try {
|
|
163
|
+
const job = JSON.parse(fs.readFileSync(path.join(home, ".pi", "piagent-update-job.json"), "utf8"));
|
|
164
|
+
if (job?.state === "running" && job.pid === process.ppid) delete env.PIAGENT_NO_UPDATE_CHECK;
|
|
165
|
+
} catch { /* no update running */ }
|
|
166
|
+
return env;
|
|
167
|
+
}
|
|
168
|
+
|
|
157
169
|
async function ensureStarted() {
|
|
158
170
|
// After an update the Gateway still running is the earlier release, with
|
|
159
171
|
// its pages loaded: it is replaced rather than reused.
|
|
@@ -174,7 +186,7 @@ async function ensureStarted() {
|
|
|
174
186
|
fileURLToPath(import.meta.url),
|
|
175
187
|
"serve",
|
|
176
188
|
...(agentDir ? ["--agent-dir", agentDir] : [])
|
|
177
|
-
], { cwd: process.cwd(), env: { ...
|
|
189
|
+
], { cwd: process.cwd(), env: { ...serveEnvironment(),
|
|
178
190
|
PIAGENT_PINNED_TS_TRANSFORM_ROOT: path.join(effectiveAgentDir, "npm", "node_modules", "pi-mcp-adapter") },
|
|
179
191
|
detached: true, stdio: "ignore" });
|
|
180
192
|
let exit = null;
|