@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.
Files changed (56) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/README.md +1 -1
  3. package/README.vi.md +1 -1
  4. package/docs/command-reference-vietnamese.md +2 -2
  5. package/docs/distribution-standard.md +7 -7
  6. package/docs/languages.json +2 -2
  7. package/docs/operator-manual-vietnamese.md +7 -7
  8. package/docs/publishing-for-teams.md +3 -0
  9. package/docs/release-install-policy.md +6 -6
  10. package/docs/security-threat-model.md +1 -1
  11. package/docs/team-onboarding.md +2 -2
  12. package/docs-site/architecture.html +1 -1
  13. package/docs-site/benchmark.html +1 -1
  14. package/docs-site/commands.html +1 -1
  15. package/docs-site/context-engine.html +1 -1
  16. package/docs-site/en/architecture.html +1 -1
  17. package/docs-site/en/benchmark.html +1 -1
  18. package/docs-site/en/commands.html +1 -1
  19. package/docs-site/en/context-engine.html +1 -1
  20. package/docs-site/en/index.html +1 -1
  21. package/docs-site/en/mcp-approval.html +1 -1
  22. package/docs-site/en/mcp-auth.html +1 -1
  23. package/docs-site/en/mcp-servers.html +1 -1
  24. package/docs-site/en/mcp.html +1 -1
  25. package/docs-site/en/permissions.html +1 -1
  26. package/docs-site/en/profiles.html +1 -1
  27. package/docs-site/en/quickstart.html +1 -1
  28. package/docs-site/en/runtime.html +1 -1
  29. package/docs-site/en/scope.html +1 -1
  30. package/docs-site/en/security.html +1 -1
  31. package/docs-site/en/team.html +1 -1
  32. package/docs-site/en/whats-new.html +1 -1
  33. package/docs-site/en/workflows.html +1 -1
  34. package/docs-site/index.html +1 -1
  35. package/docs-site/mcp-approval.html +1 -1
  36. package/docs-site/mcp-auth.html +1 -1
  37. package/docs-site/mcp-servers.html +1 -1
  38. package/docs-site/mcp.html +1 -1
  39. package/docs-site/permissions.html +1 -1
  40. package/docs-site/profiles.html +1 -1
  41. package/docs-site/quickstart.html +1 -1
  42. package/docs-site/runtime.html +1 -1
  43. package/docs-site/scope.html +1 -1
  44. package/docs-site/security.html +1 -1
  45. package/docs-site/team.html +1 -1
  46. package/docs-site/whats-new.html +1 -1
  47. package/docs-site/workflows.html +1 -1
  48. package/package.json +1 -1
  49. package/packages/piagent-core/package.json +1 -1
  50. package/packages/piagent-webui/dist/client/assets/{MarkdownMessage-BnvXTpou.js → MarkdownMessage-rD9WJKxs.js} +1 -1
  51. package/packages/piagent-webui/dist/client/assets/{index-DZg979Ht.js → index-BZRFw1u6.js} +3 -3
  52. package/packages/piagent-webui/dist/client/index.html +1 -1
  53. package/packages/piagent-webui/gateway/gateway-session-stream.ts +10 -0
  54. package/packages/piagent-webui/gateway/rpc-ui-context.ts +28 -0
  55. package/packages/piagent-webui/gateway/session-operation-start.ts +34 -0
  56. 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-DZg979Ht.js"></script>
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
- active.completion = prompt.then(() => this.#finishOperation(sessionRef, operationRef, stream),
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 Promise.race([
311
- stream.started(),
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