@piagent/platform 1.14.0 → 1.15.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 +26 -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 +24 -0
- package/docs-site/content/index.html +4 -4
- package/docs-site/content/whats-new.html +24 -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 +25 -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 +25 -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/managed/broker-client.mjs +7 -1
- package/packages/piagent-core/managed/call-purpose.mjs +39 -0
- package/packages/piagent-core/managed/helper-roles.mjs +7 -3
- package/packages/piagent-core/managed/helper-run.mjs +8 -5
- package/packages/piagent-core/managed/patch-snapshot.mjs +29 -7
- package/packages/piagent-core/managed/request-stream.mjs +4 -1
- package/packages/piagent-core/managed/review-snapshot-worker.mjs +27 -3
- package/packages/piagent-core/managed/sandbox-diagnostic.mjs +7 -1
- package/packages/piagent-core/managed/session.mjs +42 -14
- package/packages/piagent-core/managed/tool-boundary.mjs +5 -1
- package/packages/piagent-core/managed/web-search.mjs +1 -1
- package/packages/piagent-core/managed/workflow.mjs +19 -5
- package/packages/piagent-core/package.json +1 -1
- package/packages/piagent-core/runtime/managed-failure.mjs +9 -1
- package/packages/piagent-webui/dist/client/assets/{MarkdownMessage-CyT2G1H2.js → MarkdownMessage-DjA3OGyI.js} +1 -1
- package/packages/piagent-webui/dist/client/assets/{index-BBEa7Ger.js → index-bE2DKmc1.js} +12 -12
- package/packages/piagent-webui/dist/client/index.html +1 -1
- package/packages/piagent-webui/gateway/pi-host.ts +16 -2
- package/packages/piagent-webui/gateway/session-command-controller.ts +11 -0
- package/packages/piagent-webui/gateway/session-runtime-supervisor.ts +2 -2
- package/packages/piagent-webui/ownership/session-lease-store.ts +20 -1
- package/scripts/piagent-cli.mjs +5 -1
- package/scripts/update-global.mjs +7 -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-bE2DKmc1.js"></script>
|
|
11
11
|
<link rel="stylesheet" crossorigin href="/assets/index-Z91z6SlN.css">
|
|
12
12
|
</head>
|
|
13
13
|
<body>
|
|
@@ -28,9 +28,23 @@ export function installedPiHostRoot(): string {
|
|
|
28
28
|
} catch {
|
|
29
29
|
// The operator installation is normally global.
|
|
30
30
|
}
|
|
31
|
+
const windows = process.platform === "win32";
|
|
32
|
+
if (windows) {
|
|
33
|
+
// npm's global prefix is configurable (CI commonly uses C:\npm\prefix).
|
|
34
|
+
// Read the package beside each PATH entry directly: where.exe may be
|
|
35
|
+
// unavailable in a restricted launcher, and an earlier stale shim must
|
|
36
|
+
// not hide a valid installation later on PATH. Windows env keys ignore case.
|
|
37
|
+
const environment = Object.fromEntries(Object.entries(process.env).map(([key, value]) => [key.toLowerCase(), value]));
|
|
38
|
+
const prefixes = [...(environment.path ?? "").split(";"), environment.npm_config_prefix];
|
|
39
|
+
for (const value of prefixes) {
|
|
40
|
+
const prefix = value?.trim().replace(/^"(.*)"$/, "$1");
|
|
41
|
+
if (!prefix || !path.isAbsolute(prefix)) continue;
|
|
42
|
+
const found = packageRootFrom(path.join(prefix, "node_modules", "@earendil-works", "pi-coding-agent", "package.json"));
|
|
43
|
+
if (found) return found;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
31
46
|
try {
|
|
32
|
-
const
|
|
33
|
-
const executable = execFileSync(windows ? "where" : "which", ["pi"], { encoding: "utf8", timeout: 2_000, stdio: ["ignore", "pipe", "ignore"] }).trim().split(/\r?\n/)[0];
|
|
47
|
+
const executable = execFileSync(windows ? "where.exe" : "which", ["pi"], { encoding: "utf8", timeout: 2_000, stdio: ["ignore", "pipe", "ignore"] }).trim().split(/\r?\n/)[0];
|
|
34
48
|
// npm on Windows puts the pi.cmd shim in the global prefix and the package
|
|
35
49
|
// in node_modules beside it.
|
|
36
50
|
const found = packageRootFrom(fs.realpathSync(executable))
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { randomBytes } from "node:crypto";
|
|
2
|
+
import { parseFailure } from "../../piagent-core/runtime/managed-failure.mjs";
|
|
2
3
|
|
|
3
4
|
import type { Catalog, SessionRow } from "../contracts/generated/session-catalog-v1.ts";
|
|
4
5
|
import type { Receipt } from "../contracts/generated/session-command-v1.ts";
|
|
@@ -279,6 +280,16 @@ export class SessionCommandController {
|
|
|
279
280
|
try { if (noDispatchEffect) attachmentReservation.release(); else attachmentReservation.commit(); }
|
|
280
281
|
catch { /* an attempted dispatch with an unprovable reservation remains uncertain */ }
|
|
281
282
|
}
|
|
283
|
+
// The company runtime of a new or reopened conversation did not open
|
|
284
|
+
// (key, harness, Agent Watch helper): nothing was sent. Say why, by
|
|
285
|
+
// its kind, rather than "resyncing" or a bare "could not restart".
|
|
286
|
+
const company = ["session.create", "session.acquire"].includes(command.action) && /^managed-[a-z]/.test(message) ? parseFailure(message) : null;
|
|
287
|
+
if (company && company.code !== "managed-request-failed") {
|
|
288
|
+
const receipt = this.#rejected(command, after, afterRow, "unavailable", `company-${company.kind}`);
|
|
289
|
+
try { this.#store.settle(command, receipt, this.#now()); } catch { /* intent remains uncertain */ }
|
|
290
|
+
if (command.action === "session.create" && afterRow) this.#events.publish("session.changed", { catalogRevision: after.catalogRevision, session: afterRow });
|
|
291
|
+
return receipt;
|
|
292
|
+
}
|
|
282
293
|
const code = /recovery/.test(message) ? "recovery-required"
|
|
283
294
|
: /(owner-conflict|operation-conflict|runtime-busy)/.test(message) ? "owner-conflict"
|
|
284
295
|
: noDispatchEffect || command.action === "session.send" && !sendAttempted ? "unavailable" : "effect-unknown";
|
|
@@ -133,7 +133,7 @@ export class SessionRuntimeSupervisor {
|
|
|
133
133
|
const runtimeInstanceRef = `runtime_${randomBytes(24).toString("base64url")}`;
|
|
134
134
|
const prior = this.#leases.inspect(sessionRef);
|
|
135
135
|
if (prior.state === "gateway-owned" || prior.state === "terminal-owned" || prior.state === "recovery-required") {
|
|
136
|
-
try { this.#leases.releaseDeadOwnerForExplicitRecovery(sessionRef); } catch { /* live or unprovable owners stay authoritative */ }
|
|
136
|
+
try { this.#leases.releaseDeadOwnerForExplicitRecovery(sessionRef, new Date(), this.#gatewayInstanceRef); } catch { /* live or unprovable owners stay authoritative */ }
|
|
137
137
|
}
|
|
138
138
|
const lease = this.#leases.acquire(sessionRef, this.#gatewayInstanceRef, runtimeInstanceRef);
|
|
139
139
|
try {
|
|
@@ -161,7 +161,7 @@ export class SessionRuntimeSupervisor {
|
|
|
161
161
|
const active = this.#active.get(sessionRef);
|
|
162
162
|
if (!active) {
|
|
163
163
|
const current = this.#leases.inspect(sessionRef);
|
|
164
|
-
if (current.state === "released") return
|
|
164
|
+
if (current.state === "released" || current.state === "recovery-required" && current.gatewayInstanceRef === this.#gatewayInstanceRef) return this.#leases.releaseOwnRecovery(sessionRef, this.#gatewayInstanceRef);
|
|
165
165
|
throw new Error(current.state === "recovery-required" ? "session-recovery-required" : "session-owner-conflict");
|
|
166
166
|
}
|
|
167
167
|
if (active.operationRef) throw new Error("session-runtime-busy");
|
|
@@ -253,8 +253,10 @@ export class SessionLeaseStore {
|
|
|
253
253
|
return this.#acquireOwner(sessionRef, gatewayInstanceRef, runtimeInstanceRef, now);
|
|
254
254
|
}
|
|
255
255
|
|
|
256
|
-
|
|
256
|
+
// `ownGateway`: the calling Gateway, whose own recovery it may release.
|
|
257
|
+
releaseDeadOwnerForExplicitRecovery(sessionRef: string, now = new Date(), ownGateway?: string): SessionLeaseSnapshot {
|
|
257
258
|
if (!Number.isFinite(now.getTime())) throw new Error("session-lease-input-invalid");
|
|
259
|
+
if (ownGateway && this.inspect(sessionRef).state === "recovery-required" && this.inspect(sessionRef).gatewayInstanceRef === ownGateway) return this.releaseOwnRecovery(sessionRef, ownGateway, now);
|
|
258
260
|
return this.#withMutationLock(sessionRef, () => {
|
|
259
261
|
const current = this.inspect(sessionRef);
|
|
260
262
|
const terminalOwner = current.state === "terminal-owned"
|
|
@@ -329,6 +331,23 @@ export class SessionLeaseStore {
|
|
|
329
331
|
});
|
|
330
332
|
}
|
|
331
333
|
|
|
334
|
+
// A Gateway whose own runtime failed to open or to close left the lease in
|
|
335
|
+
// recovery under its own name. Nothing of it runs (the Gateway knows its
|
|
336
|
+
// live runtimes), so that Gateway may release it: archiving or reopening
|
|
337
|
+
// must not wait for the Gateway itself to exit. Another owner's recovery
|
|
338
|
+
// still needs proof that the owner is gone.
|
|
339
|
+
releaseOwnRecovery(sessionRef: string, gatewayInstanceRef: string, now = new Date()): SessionLeaseSnapshot {
|
|
340
|
+
if (!validRef(gatewayInstanceRef) || gatewayInstanceRef.startsWith(TERMINAL_OWNER_PREFIX) || !Number.isFinite(now.getTime())) throw new Error("session-lease-input-invalid");
|
|
341
|
+
return this.#withMutationLock(sessionRef, () => {
|
|
342
|
+
const current = this.inspect(sessionRef);
|
|
343
|
+
if (current.state === "released") return current;
|
|
344
|
+
if (current.state !== "recovery-required" || current.gatewayInstanceRef !== gatewayInstanceRef) throw new Error("session-recovery-required");
|
|
345
|
+
this.#append(sessionRef, { recordedAt: now.toISOString(), sessionRef, event: "released", ownerEpoch: current.ownerEpoch!,
|
|
346
|
+
gatewayInstanceRef, runtimeInstanceRef: current.runtimeInstanceRef!, reasonCode: null });
|
|
347
|
+
return this.inspect(sessionRef);
|
|
348
|
+
});
|
|
349
|
+
}
|
|
350
|
+
|
|
332
351
|
requireRecovery(sessionRef: string, ownerEpoch: string, gatewayInstanceRef: string, runtimeInstanceRef: string,
|
|
333
352
|
reasonCode: string, now = new Date()): SessionLeaseSnapshot {
|
|
334
353
|
if (!validRef(reasonCode)) throw new Error("session-lease-reason-invalid");
|
package/scripts/piagent-cli.mjs
CHANGED
|
@@ -51,6 +51,10 @@ if (invokedAs === "piagent") {
|
|
|
51
51
|
} else if (subcommand === "select-verification") {
|
|
52
52
|
script = "scripts/select-independent-verification.mjs";
|
|
53
53
|
forwardedArgs = forwardedArgs.slice(1);
|
|
54
|
+
} else if (subcommand && Object.hasOwn(scriptByCommand, `piagent-${subcommand}`)) {
|
|
55
|
+
// `piagent doctor` is `piagent-doctor`: members type it both ways.
|
|
56
|
+
script = scriptByCommand[`piagent-${subcommand}`];
|
|
57
|
+
forwardedArgs = forwardedArgs.slice(1);
|
|
54
58
|
} else if ([undefined, "help", "--help", "-h"].includes(subcommand)) {
|
|
55
59
|
// Subcommands are listed, their flags are not. Restating them here meant a
|
|
56
60
|
// second copy that drifts: this line still advertised the dashboard without
|
|
@@ -70,7 +74,7 @@ if (invokedAs === "piagent") {
|
|
|
70
74
|
}
|
|
71
75
|
|
|
72
76
|
if (!script) {
|
|
73
|
-
console.error(`Unknown Pi Agent command: ${invokedAs || "(unknown)"}`);
|
|
77
|
+
console.error(`Unknown Pi Agent command: ${[invokedAs || "(unknown)", invokedAs === "piagent" ? forwardedArgs[0] : ""].filter(Boolean).join(" ")}`);
|
|
74
78
|
console.error(`Expected one of: ${Object.keys(scriptByCommand).sort().join(", ")}`);
|
|
75
79
|
process.exit(2);
|
|
76
80
|
}
|
|
@@ -423,7 +423,8 @@ function main() {
|
|
|
423
423
|
fail(`${HELPER_PACKAGE}@${targetHelper} does not pin an exact ${HOST_PACKAGE} version, so the host cannot be updated safely.`);
|
|
424
424
|
}
|
|
425
425
|
|
|
426
|
-
|
|
426
|
+
// npm exec is temporary even when its version already matches the target.
|
|
427
|
+
const helperNeedsChange = options.force || currentHelper !== targetHelper || isNpxCacheRoot(platformRoot);
|
|
427
428
|
const hostNeedsChange = options.host && (options.force || currentHost !== targetHost);
|
|
428
429
|
|
|
429
430
|
console.log("Pi Agent Platform update");
|
|
@@ -475,6 +476,11 @@ function main() {
|
|
|
475
476
|
}
|
|
476
477
|
}
|
|
477
478
|
|
|
479
|
+
// Refuse an impossible package installation before replacing either global.
|
|
480
|
+
if (options.package && !options.host && effectiveHost !== targetHost) {
|
|
481
|
+
fail(`--no-host cannot install this Pi package: active Pi is ${effectiveHost ?? "missing"}, but v${targetHelper} requires ${targetHost}. Rerun without --no-host.`);
|
|
482
|
+
}
|
|
483
|
+
|
|
478
484
|
// Host first: piagent-install refuses to run against a host that does not match
|
|
479
485
|
// the version its package.json pins, so a helper installed ahead of the host
|
|
480
486
|
// would fail on its very next step.
|