@piagent/platform 1.28.1 → 1.28.3
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 +25 -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/managed/binding-wait.mjs +87 -0
- package/packages/piagent-core/package.json +1 -1
- package/packages/piagent-webui/dist/client/assets/{MarkdownMessage-BpNzOP6f.js → MarkdownMessage-3vQTaeOO.js} +1 -1
- package/packages/piagent-webui/dist/client/assets/{index-DWkb1mpZ.js → index-Dve4GNQ7.js} +4 -4
- package/packages/piagent-webui/dist/client/index.html +1 -1
- package/packages/piagent-webui/gateway/managed-launch.ts +20 -3
- package/packages/piagent-webui/shared/company-copy.ts +2 -0
- package/scripts/company-terminal.mjs +1 -1
- package/scripts/piagent-studio.mjs +24 -6
|
@@ -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-Dve4GNQ7.js"></script>
|
|
11
11
|
<link rel="stylesheet" crossorigin href="/assets/index-B5XOKQy-.css">
|
|
12
12
|
</head>
|
|
13
13
|
<body>
|
|
@@ -7,16 +7,18 @@ import { fileURLToPath } from "node:url";
|
|
|
7
7
|
import { requestGatewayControl } from "./control-socket.ts";
|
|
8
8
|
import { gatewayProfileState } from "./profile-state.ts";
|
|
9
9
|
import { agentWatchDataDirectory, interopEnvironment, storedSlot } from "../../piagent-core/managed/store.mjs";
|
|
10
|
+
import { agentWatchRunning, awaitRebind, bindingMismatch, brokerUpdatedAfterBinding } from "../../piagent-core/managed/binding-wait.mjs";
|
|
10
11
|
|
|
11
12
|
// The personal dashboard shows company sessions but never runs them: it starts
|
|
12
13
|
// the pinned managed entrypoint (its own process and Gateway, `--serve`) the
|
|
13
14
|
// way Agent Watch's launcher does, then relays to it. Grants stay inside that
|
|
14
15
|
// process; this process only reads the public binding.
|
|
15
16
|
export type ManagedLaunchStatus = { schemaVersion: 1; version: "piagent-managed-launch-status-v1"; available: boolean; model: "agent-watch-auto" };
|
|
16
|
-
type Binding = { file: string; profile: string; node: string; entrypoint: string; broker: string; brokerSha256: string };
|
|
17
|
+
type Binding = { file: string; profile: string; node: string; entrypoint: string; broker: string; brokerSha256: string; config: Record<string, unknown> };
|
|
17
18
|
|
|
18
19
|
const CLEAN_ENV = { PATH: "/usr/bin:/bin", LANG: "en_US.UTF-8", TERM: "xterm-256color", ...interopEnvironment() };
|
|
19
20
|
const READY = /^Agent Watch Auto: ready$/m;
|
|
21
|
+
const BINDING_WAIT = /\[managed-binding-wait\]$/m, BINDING_WAIT_MS = 95_000;
|
|
20
22
|
const sha256 = (file: string) => createHash("sha256").update(fs.readFileSync(file)).digest("hex");
|
|
21
23
|
const HEX64 = /^[a-f0-9]{64}$/;
|
|
22
24
|
|
|
@@ -40,7 +42,7 @@ function binding(agentDir: string): Binding | null {
|
|
|
40
42
|
const absolute = (entry: unknown) => typeof entry === "string" && path.isAbsolute(entry);
|
|
41
43
|
if (value?.schema_version !== 1 || value.model !== "agent-watch-auto" || !HEX64.test(String(value.profile_id))
|
|
42
44
|
|| !absolute(value.node) || !absolute(value.entrypoint) || !absolute(value.broker) || !HEX64.test(String(value.broker_sha256))) return null;
|
|
43
|
-
return { file, profile: value.profile_id, node: value.node, entrypoint: value.entrypoint, broker: value.broker, brokerSha256: value.broker_sha256 };
|
|
45
|
+
return { file, profile: value.profile_id, node: value.node, entrypoint: value.entrypoint, broker: value.broker, brokerSha256: value.broker_sha256, config: value };
|
|
44
46
|
} catch { return null; }
|
|
45
47
|
}
|
|
46
48
|
|
|
@@ -116,6 +118,17 @@ async function start(agentDir: string, home: string, timeoutMs: number): Promise
|
|
|
116
118
|
await new Promise((resolve) => setTimeout(resolve, 250));
|
|
117
119
|
}
|
|
118
120
|
}
|
|
121
|
+
// Right after an Agent Watch update only the broker's hash differs until
|
|
122
|
+
// Watch writes the new binding (up to ~70 s after it relaunches): wait for
|
|
123
|
+
// it, as the entrypoint does, then start over on the new binding. The
|
|
124
|
+
// broker never runs while any pinned hash differs.
|
|
125
|
+
const pins = (next: Binding) => bindingMismatch(next.config, { self: next.entrypoint, node: next.node });
|
|
126
|
+
if (pins(pinned) === "broker" && brokerUpdatedAfterBinding(pinned.file, pinned.broker)) {
|
|
127
|
+
const rebound = await awaitRebind({ running: () => agentWatchRunning(pinned.broker),
|
|
128
|
+
current: () => { const next = binding(agentDir); return next && pins(next) === null ? next : null; } });
|
|
129
|
+
if (!rebound) throw new Error(agentWatchRunning(pinned.broker) === false ? "managed-binding-watch-not-running" : "managed-binding-update-pending");
|
|
130
|
+
return start(agentDir, home, timeoutMs);
|
|
131
|
+
}
|
|
119
132
|
await authorize(pinned);
|
|
120
133
|
// Startup failures are reported on stderr as `Agent Watch: managed-…`; the
|
|
121
134
|
// log is private and removed once the process is ready or has exited.
|
|
@@ -130,8 +143,12 @@ async function start(agentDir: string, home: string, timeoutMs: number): Promise
|
|
|
130
143
|
child.once("exit", () => { exited = true; }); child.once("error", () => { exited = true; }); child.unref();
|
|
131
144
|
} finally { fs.closeSync(fd); }
|
|
132
145
|
try {
|
|
133
|
-
|
|
146
|
+
// Right after an update the entrypoint waits (up to 90 s) for Agent Watch
|
|
147
|
+
// to write the new binding; give it that long before timing out.
|
|
148
|
+
let limit = timeoutMs;
|
|
149
|
+
for (const started = Date.now(); Date.now() - started < limit;) {
|
|
134
150
|
const text = fs.readFileSync(log, "utf8");
|
|
151
|
+
if (limit === timeoutMs && BINDING_WAIT.test(text)) limit = timeoutMs + BINDING_WAIT_MS;
|
|
135
152
|
// The launch may just have pointed this slot at the member's store.
|
|
136
153
|
if (READY.test(text)) return controlSocket(home, pinned.profile);
|
|
137
154
|
if (exited) throw new Error(text.match(/Agent Watch: (managed-[a-z:_-]+)\./)?.[1] ?? "managed-launch-failed");
|
|
@@ -12,6 +12,8 @@ export const LAUNCH_REASONS: Record<string, [string, string]> = {
|
|
|
12
12
|
"managed-key-unavailable": ["Agent Watch không đọc được key công ty trên máy này. Mở Agent Watch, dán lại mã kết nối, bấm Kết nối Piagent trong WSL rồi kết nối lại.", "Agent Watch cannot read the company key on this computer. Open Agent Watch, paste the connection code again, choose Connect Piagent in WSL, then connect again."],
|
|
13
13
|
"managed-profile-disconnected": ["Cấu hình Piagent đang thuộc key công ty đã ngắt kết nối. Nhập lại cấu hình cho Piagent trong Agent Watch (trên Windows: Kết nối Piagent trong WSL).", "The Piagent configuration belongs to a disconnected company key. Import it again in Agent Watch (on Windows: Connect Piagent in WSL)."],
|
|
14
14
|
"managed-launch-binding-changed": ["Agent Watch hoặc Piagent vừa cập nhật. Nhập lại cấu hình cho Piagent trong Agent Watch (trên Windows: Kết nối Piagent trong WSL).", "Agent Watch or Piagent changed. Import the Piagent configuration again in Agent Watch (on Windows: Connect Piagent in WSL)."],
|
|
15
|
+
"managed-binding-update-pending": ["Agent Watch đang chạy nhưng chưa cập nhật cấu hình sau bản Piagent hoặc Agent Watch mới. Mở Agent Watch → Studio, bấm Áp dụng cho Piagent rồi mở lại.", "Agent Watch is running but has not updated the configuration after the Piagent or Agent Watch update yet. Open Agent Watch → Studio, apply for Piagent, then open it again."],
|
|
16
|
+
"managed-binding-watch-not-running": ["Piagent hoặc Agent Watch vừa cập nhật nhưng Agent Watch không chạy nên chưa cập nhật cấu hình. Mở Agent Watch, chờ vài giây rồi mở lại.", "Piagent or Agent Watch was updated but Agent Watch is not running, so the configuration was not updated. Open Agent Watch, wait a few seconds, then open it again."],
|
|
15
17
|
"managed-launch-version-mismatch": ["Agent Watch đang dùng một bản Piagent khác với dashboard này. Mở Agent Watch → Studio, bấm Áp dụng cho Piagent rồi kết nối lại.", "Agent Watch points at another Piagent install than this dashboard. Open Agent Watch → Studio, apply for Piagent, then connect again."],
|
|
16
18
|
"managed-launch-stale-runtime": ["Phiên công ty của bản Piagent cũ chưa dừng được. Thử kết nối lại sau ít giây.", "The company runtime from the previous Piagent did not stop yet. Connect again in a few seconds."],
|
|
17
19
|
"managed-launch-timeout": ["Phiên công ty khởi động quá lâu. Kết nối lại.", "The company runtime took too long to start. Connect again."],
|
|
@@ -68,7 +68,7 @@ export function companyTerminalExtension({ managed, packageRoot, sdkRoot, bindin
|
|
|
68
68
|
};
|
|
69
69
|
|
|
70
70
|
registerTerminalUpdateOffer(pi, { installed, packageRoot, restart: 'piagent studio',
|
|
71
|
-
afterUpdate: () => bindingChanged() ? 'Bản mới đổi trình khởi chạy công ty:
|
|
71
|
+
afterUpdate: () => bindingChanged() ? 'Bản mới đổi trình khởi chạy công ty: Agent Watch tự cập nhật cấu hình (cần Agent Watch đang chạy); piagent studio chờ việc này khi mở lại.' : null });
|
|
72
72
|
|
|
73
73
|
// Ask first or Bypass, kept with the conversation like the dashboard's switch.
|
|
74
74
|
const showPermission = (ctx) => ctx.ui?.setStatus?.(PERMISSION_STATUS, managed()?.permission === 'trusted-full-access' ? 'Bypass' : undefined);
|
|
@@ -7,6 +7,7 @@ import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
|
7
7
|
import { ManagedBrokerClient } from '../packages/piagent-core/managed/broker-client.mjs';
|
|
8
8
|
import { ManagedSession } from '../packages/piagent-core/managed/session.mjs';
|
|
9
9
|
import { agentWatchDataDirectory, interopEnvironment, resolveStore, storedSlot } from '../packages/piagent-core/managed/store.mjs';
|
|
10
|
+
import { agentWatchRunning, awaitRebind, bindingMismatch, brokerUpdatedAfterBinding, installedAfterBinding } from '../packages/piagent-core/managed/binding-wait.mjs';
|
|
10
11
|
|
|
11
12
|
const digest = file => createHash('sha256').update(fs.readFileSync(file)).digest('hex');
|
|
12
13
|
// Node's experimental-feature notices (type stripping, SQLite) are not
|
|
@@ -30,18 +31,35 @@ Object.assign(process.env, clean);
|
|
|
30
31
|
let broker, current, gateway, hold;
|
|
31
32
|
try {
|
|
32
33
|
const configPath = fs.realpathSync(flags.get('--config') ?? path.join(home,'.pi/agent/agent-watch-managed.json'));
|
|
33
|
-
|
|
34
|
-
|
|
34
|
+
const readConfig = () => {
|
|
35
|
+
if (!fs.statSync(configPath).isFile() || fs.statSync(configPath).size > 16384) throw Error('managed-config-invalid');
|
|
36
|
+
return JSON.parse(fs.readFileSync(configPath));
|
|
37
|
+
};
|
|
38
|
+
let config = readConfig();
|
|
35
39
|
// Agent Watch rewrites this when the key or its harness changes.
|
|
36
40
|
const bindingRevision = () => { try { return String(JSON.parse(fs.readFileSync(configPath, 'utf8')).configuration_revision ?? '') || null; } catch { return null; } };
|
|
37
41
|
// --serve backs the personal dashboard: no browser, no project of its own.
|
|
38
42
|
// Its startup probe runs in an empty private folder that is never listed.
|
|
39
43
|
const serve = flags.has('--serve');
|
|
40
44
|
if (serve && (flags.has('--project') || flags.has('--web') || flags.has('--check') || flags.has('--prompt'))) throw Error('managed-launch-arguments');
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
+
const running = { self: fs.realpathSync(fileURLToPath(import.meta.url)), node: fs.realpathSync(process.execPath) };
|
|
46
|
+
const mismatch = bindingMismatch(config, running);
|
|
47
|
+
// Right after a Piagent update only the launcher's hash differs, right after
|
|
48
|
+
// an Agent Watch update only the broker's, until Agent Watch writes the new
|
|
49
|
+
// binding: wait for it. Nothing runs before every pinned path and hash
|
|
50
|
+
// matches again.
|
|
51
|
+
const updated = mismatch === 'entrypoint' ? installedAfterBinding(configPath, config.entrypoint)
|
|
52
|
+
: mismatch === 'broker' && brokerUpdatedAfterBinding(configPath, config.broker);
|
|
53
|
+
if (updated) {
|
|
54
|
+
const watch = () => agentWatchRunning(config.broker), what = mismatch === 'broker' ? 'Agent Watch' : 'Piagent';
|
|
55
|
+
const rebound = await awaitRebind({ running: watch,
|
|
56
|
+
current: () => { try { const next = readConfig(); return bindingMismatch(next, running) === null ? next : null; } catch { return null; } },
|
|
57
|
+
waiting: state => process.stderr.write(state === false
|
|
58
|
+
? `${what} vừa cập nhật nhưng Agent Watch không chạy. Mở Agent Watch; Piagent tự tiếp tục khi cấu hình được cập nhật… [managed-binding-wait]\n`
|
|
59
|
+
: `Agent Watch đang cập nhật cấu hình cho bản ${what} mới… [managed-binding-wait]\n`) });
|
|
60
|
+
if (!rebound) throw Error(watch() === false ? 'managed-binding-watch-not-running' : 'managed-binding-update-pending');
|
|
61
|
+
config = rebound;
|
|
62
|
+
} else if (mismatch) throw Error('managed-launch-binding-changed');
|
|
45
63
|
// What company sessions need on this machine, checked with the real sandbox
|
|
46
64
|
// and broker; no conversation, no model request.
|
|
47
65
|
if (flags.has('--doctor')) {
|