@piagent/platform 1.5.3 → 1.5.4
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 +21 -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 +7 -7
- package/docs/languages.json +2 -2
- package/docs/operator-manual-vietnamese.md +7 -7
- package/docs/publishing-for-teams.md +3 -0
- package/docs/release-install-policy.md +6 -6
- package/docs/security-threat-model.md +1 -1
- 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/context-engine.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/index.html +1 -1
- 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 +1 -1
- package/docs-site/en/workflows.html +1 -1
- package/docs-site/index.html +1 -1
- 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 +1 -1
- package/docs-site/workflows.html +1 -1
- package/package.json +1 -1
- package/packages/piagent-core/package.json +1 -1
- package/packages/piagent-webui/dist/client/assets/{MarkdownMessage-BnvXTpou.js → MarkdownMessage-rD9WJKxs.js} +1 -1
- package/packages/piagent-webui/dist/client/assets/{index-DZg979Ht.js → index-BZRFw1u6.js} +3 -3
- package/packages/piagent-webui/dist/client/index.html +1 -1
- package/packages/piagent-webui/gateway/gateway-session-stream.ts +10 -0
- package/packages/piagent-webui/gateway/rpc-ui-context.ts +28 -0
- package/packages/piagent-webui/gateway/session-operation-start.ts +34 -0
- package/packages/piagent-webui/gateway/session-runtime-supervisor.ts +13 -33
|
@@ -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-BZRFw1u6.js"></script>
|
|
11
11
|
<link rel="stylesheet" crossorigin href="/assets/index-BV-_03yl.css">
|
|
12
12
|
</head>
|
|
13
13
|
<body>
|
|
@@ -31,6 +31,8 @@ export class GatewaySessionStream {
|
|
|
31
31
|
#truncated = false;
|
|
32
32
|
#started = false;
|
|
33
33
|
#startListeners: Array<() => void> = [];
|
|
34
|
+
#settled = false;
|
|
35
|
+
#settleListeners: Array<() => void> = [];
|
|
34
36
|
readonly #toolRefs = new Map<string, string>();
|
|
35
37
|
|
|
36
38
|
constructor(options: { sessionRef: string; operationRef: string; events: GatewayEventStore }) {
|
|
@@ -42,10 +44,18 @@ export class GatewaySessionStream {
|
|
|
42
44
|
return new Promise((resolve) => this.#startListeners.push(resolve));
|
|
43
45
|
}
|
|
44
46
|
|
|
47
|
+
settled(): Promise<void> {
|
|
48
|
+
if (this.#settled) return Promise.resolve();
|
|
49
|
+
return new Promise((resolve) => this.#settleListeners.push(resolve));
|
|
50
|
+
}
|
|
51
|
+
|
|
45
52
|
observe(event: any): void {
|
|
46
53
|
if (event?.type === "agent_start") {
|
|
47
54
|
this.#started = true; for (const resolve of this.#startListeners.splice(0)) resolve(); return;
|
|
48
55
|
}
|
|
56
|
+
if (event?.type === "agent_settled") {
|
|
57
|
+
this.#settled = true; for (const resolve of this.#settleListeners.splice(0)) resolve(); return;
|
|
58
|
+
}
|
|
49
59
|
if (event?.type === "message_start" && event.message?.role === "assistant") {
|
|
50
60
|
this.#messageRef = ref("message"); this.#buffer = ""; return;
|
|
51
61
|
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { GATEWAY_RUNTIME_UI_MARKER } from "../ownership/gateway-runtime-context.ts";
|
|
2
|
+
|
|
3
|
+
export function rpcUiContext(): object {
|
|
4
|
+
const plain = (text: unknown): string => String(text ?? "");
|
|
5
|
+
const theme = Object.freeze({
|
|
6
|
+
fg: (_color: unknown, text: unknown) => plain(text),
|
|
7
|
+
bg: (_color: unknown, text: unknown) => plain(text),
|
|
8
|
+
bold: plain,
|
|
9
|
+
italic: plain,
|
|
10
|
+
underline: plain,
|
|
11
|
+
inverse: plain,
|
|
12
|
+
strikethrough: plain,
|
|
13
|
+
getFgAnsi: () => "",
|
|
14
|
+
getBgAnsi: () => "",
|
|
15
|
+
getColorMode: () => "truecolor",
|
|
16
|
+
getThinkingBorderColor: () => plain,
|
|
17
|
+
getBashModeBorderColor: () => plain
|
|
18
|
+
});
|
|
19
|
+
return new Proxy({}, {
|
|
20
|
+
get(_target, property) {
|
|
21
|
+
if (property === GATEWAY_RUNTIME_UI_MARKER) return true;
|
|
22
|
+
if (property === "theme") return theme;
|
|
23
|
+
if (property === "confirm") return () => new Promise<boolean>(() => undefined);
|
|
24
|
+
if (property === "select" || property === "input") return async () => undefined;
|
|
25
|
+
return () => undefined;
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { GatewaySessionStream } from "./gateway-session-stream.ts";
|
|
2
|
+
|
|
3
|
+
const DEFERRED_OPERATION_START_GRACE_MS = 5_000;
|
|
4
|
+
|
|
5
|
+
export async function waitForOperationStart(
|
|
6
|
+
stream: GatewaySessionStream,
|
|
7
|
+
prompt: Promise<void>
|
|
8
|
+
): Promise<"prompt-owned" | "deferred"> {
|
|
9
|
+
const started = stream.started();
|
|
10
|
+
const first = await Promise.race([
|
|
11
|
+
started.then(() => ({ state: "started" as const })),
|
|
12
|
+
prompt.then(() => ({ state: "prompt-resolved" as const }),
|
|
13
|
+
(error: unknown) => ({ state: "prompt-rejected" as const, error }))
|
|
14
|
+
]);
|
|
15
|
+
if (first.state === "started") return "prompt-owned";
|
|
16
|
+
if (first.state === "prompt-rejected") throw first.error;
|
|
17
|
+
|
|
18
|
+
// Extension commands such as `/workflow` launch their agent turn through
|
|
19
|
+
// `pi.sendUserMessage()`. The outer prompt resolves before that deferred turn
|
|
20
|
+
// emits `agent_start`, so retain ownership until the real operation appears.
|
|
21
|
+
let timer: ReturnType<typeof setTimeout> | undefined;
|
|
22
|
+
try {
|
|
23
|
+
await Promise.race([
|
|
24
|
+
started,
|
|
25
|
+
new Promise<never>((_resolve, reject) => {
|
|
26
|
+
timer = setTimeout(() => reject(new Error("session-operation-start-unobserved")),
|
|
27
|
+
DEFERRED_OPERATION_START_GRACE_MS);
|
|
28
|
+
})
|
|
29
|
+
]);
|
|
30
|
+
return "deferred";
|
|
31
|
+
} finally {
|
|
32
|
+
if (timer) clearTimeout(timer);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
@@ -11,9 +11,10 @@ import { isUserConversationSession, projectRefForCwd, sessionRefForPath } from "
|
|
|
11
11
|
import { SessionLeaseStore, type SessionLeaseSnapshot } from "./session-lease-store.ts";
|
|
12
12
|
import { GatewayEventStore } from "./gateway-events.ts";
|
|
13
13
|
import { GatewaySessionStream } from "./gateway-session-stream.ts";
|
|
14
|
-
import { GATEWAY_RUNTIME_UI_MARKER } from "../ownership/gateway-runtime-context.ts";
|
|
15
14
|
import { preferAuthoritativePiagentGuard } from "./extension-authority.ts";
|
|
16
15
|
import { executePermissionCommand, executeRuntimeCommand } from "./runtime-session-controls.ts";
|
|
16
|
+
import { rpcUiContext } from "./rpc-ui-context.ts";
|
|
17
|
+
import { waitForOperationStart } from "./session-operation-start.ts";
|
|
17
18
|
|
|
18
19
|
const MAX_WARM_RUNTIMES = 10;
|
|
19
20
|
|
|
@@ -25,33 +26,6 @@ type ActiveRuntime = { runtime: RuntimeHandle; lease: SessionLeaseSnapshot; info
|
|
|
25
26
|
type Projection = { sessionRevision: string; liveState: "offline" | "idle" | "running" | "paused" | "waiting-approval" | "uncertain" };
|
|
26
27
|
type RuntimeCommandResult = Awaited<ReturnType<typeof executeRuntimeCommand>>;
|
|
27
28
|
|
|
28
|
-
function rpcUiContext(): object {
|
|
29
|
-
const plain = (text: unknown): string => String(text ?? "");
|
|
30
|
-
const theme = Object.freeze({
|
|
31
|
-
fg: (_color: unknown, text: unknown) => plain(text),
|
|
32
|
-
bg: (_color: unknown, text: unknown) => plain(text),
|
|
33
|
-
bold: plain,
|
|
34
|
-
italic: plain,
|
|
35
|
-
underline: plain,
|
|
36
|
-
inverse: plain,
|
|
37
|
-
strikethrough: plain,
|
|
38
|
-
getFgAnsi: () => "",
|
|
39
|
-
getBgAnsi: () => "",
|
|
40
|
-
getColorMode: () => "truecolor",
|
|
41
|
-
getThinkingBorderColor: () => plain,
|
|
42
|
-
getBashModeBorderColor: () => plain
|
|
43
|
-
});
|
|
44
|
-
return new Proxy({}, {
|
|
45
|
-
get(_target, property) {
|
|
46
|
-
if (property === GATEWAY_RUNTIME_UI_MARKER) return true;
|
|
47
|
-
if (property === "theme") return theme;
|
|
48
|
-
if (property === "confirm") return () => new Promise<boolean>(() => undefined);
|
|
49
|
-
if (property === "select" || property === "input") return async () => undefined;
|
|
50
|
-
return () => undefined;
|
|
51
|
-
}
|
|
52
|
-
});
|
|
53
|
-
}
|
|
54
|
-
|
|
55
29
|
function productionRuntimeFactory(options: { host: any; agentDir: string; packageRoot: string; modelRuntime?: any }): RuntimeFactory {
|
|
56
30
|
return async (info, _runtimeInstanceRef, sessionManager) => {
|
|
57
31
|
const guard = path.join(options.packageRoot, "packages", "piagent-core", "extensions", "piagent-guard.ts");
|
|
@@ -305,12 +279,18 @@ export class SessionRuntimeSupervisor {
|
|
|
305
279
|
let prompt: Promise<void>;
|
|
306
280
|
try { prompt = session.prompt(payload.message, payload.images?.length ? { images: payload.images } : undefined); }
|
|
307
281
|
catch (error) { await this.#finishOperation(sessionRef, operationRef, stream); throw error; }
|
|
308
|
-
|
|
282
|
+
const started = waitForOperationStart(stream, prompt);
|
|
283
|
+
// A normal prompt resolves after the agent settles. A workflow extension
|
|
284
|
+
// command resolves before its nested agent turn starts. In both cases the
|
|
285
|
+
// canonical completion boundary is `agent_settled`, not the outer promise.
|
|
286
|
+
const lifecycle = started.then(async (mode) => {
|
|
287
|
+
if (mode === "deferred") await stream.settled();
|
|
288
|
+
else await Promise.race([stream.settled(), prompt]);
|
|
289
|
+
});
|
|
290
|
+
active.completion = lifecycle.then(() => this.#finishOperation(sessionRef, operationRef, stream),
|
|
309
291
|
() => this.#finishOperation(sessionRef, operationRef, stream));
|
|
310
|
-
await
|
|
311
|
-
|
|
312
|
-
prompt.then(() => { throw new Error("session-operation-start-unobserved"); }, (error: unknown) => { throw error; })
|
|
313
|
-
]);
|
|
292
|
+
try { await started; }
|
|
293
|
+
catch (error) { await active.completion; throw error; }
|
|
314
294
|
return { resultCode: "started", operationRef };
|
|
315
295
|
}
|
|
316
296
|
|