@piagent/platform 1.22.0 → 1.23.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 +24 -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 +15 -0
- package/docs-site/content/index.html +4 -4
- package/docs-site/content/whats-new.html +15 -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 +16 -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 +16 -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/extensions/task-state.js +10 -10
- package/packages/piagent-core/extensions/workspace-evidence-roots.js +21 -1
- package/packages/piagent-core/managed/fast-mode.mjs +66 -0
- package/packages/piagent-core/managed/helper-run.mjs +2 -1
- package/packages/piagent-core/managed/request-stream.mjs +10 -2
- package/packages/piagent-core/managed/session.mjs +8 -1
- package/packages/piagent-core/managed/tool-failures.mjs +91 -0
- package/packages/piagent-core/package.json +1 -1
- package/packages/piagent-core/runtime/inspection/webui-snapshot.ts +2 -0
- package/packages/piagent-core/runtime/managed-failure.mjs +3 -0
- package/packages/piagent-webui/dist/client/assets/{MarkdownMessage-BYa-Jveq.js → MarkdownMessage-lWHlCJq4.js} +1 -1
- package/packages/piagent-webui/dist/client/assets/index-CDVdO4C7.js +231 -0
- package/packages/piagent-webui/dist/client/index.html +1 -1
- package/packages/piagent-webui/gateway/company-relay.ts +56 -7
- package/packages/piagent-webui/gateway/managed-gateway.mjs +4 -2
- package/packages/piagent-webui/gateway/managed-projection.ts +3 -1
- package/packages/piagent-webui/gateway/runtime-session-controls.ts +9 -0
- package/packages/piagent-webui/gateway/session-catalog.ts +3 -2
- package/packages/piagent-webui/gateway/session-command-controller.ts +6 -3
- package/packages/piagent-webui/gateway/session-command-store.ts +3 -3
- package/packages/piagent-webui/gateway/session-effective-options.ts +6 -1
- package/packages/piagent-webui/gateway/session-runtime-supervisor.ts +9 -9
- package/packages/piagent-webui/shared/company-copy.ts +2 -1
- package/schemas/piagent-webui/session-catalog-v1.schema.json +1 -1
- package/schemas/piagent-webui/session-command-v1.schema.json +20 -5
- package/scripts/company-terminal.mjs +22 -0
- package/packages/piagent-webui/dist/client/assets/index-coozlgfe.js +0 -231
|
@@ -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-CDVdO4C7.js"></script>
|
|
11
11
|
<link rel="stylesheet" crossorigin href="/assets/index-ZLemRWyJ.css">
|
|
12
12
|
</head>
|
|
13
13
|
<body>
|
|
@@ -31,6 +31,13 @@ const KINDS = new Set<GatewayEventKind>(["catalog.changed", "session.changed", "
|
|
|
31
31
|
"message.completed", "operation.settled", "tool.started", "tool.completed"]);
|
|
32
32
|
const LOOPBACK = /^http:\/\/127\.0\.0\.1:\d{1,5}$/;
|
|
33
33
|
const CATALOG_REUSE_MS = 5_000;
|
|
34
|
+
// A company Gateway that takes the connection and never answers (found on
|
|
35
|
+
// Windows/WSL) kept every list of the dashboard loading: the browser waits for
|
|
36
|
+
// the catalog without a limit, and the catalog waited for the company rows.
|
|
37
|
+
// A read is given this long, a change as long as a command; a Gateway that
|
|
38
|
+
// missed one is left alone for a while, not asked again by every read.
|
|
39
|
+
const READ_TIMEOUT_MS = 15_000, ANSWER_TIMEOUT_MS = 5_000, CHANGE_TIMEOUT_MS = 120_000, STALL_RETRY_MS = 60_000;
|
|
40
|
+
const timedOut = (error: unknown) => error instanceof Error && (error.name === "TimeoutError" || error.name === "AbortError");
|
|
34
41
|
const failure = (error: unknown, fallback: string) => error instanceof Error && /^[a-z][a-z0-9:._-]{2,95}$/.test(error.message) ? error.message : fallback;
|
|
35
42
|
|
|
36
43
|
export class CompanyRelay {
|
|
@@ -54,11 +61,15 @@ export class CompanyRelay {
|
|
|
54
61
|
#retry: NodeJS.Timeout | null = null;
|
|
55
62
|
#closed = false;
|
|
56
63
|
#lastAttach = 0;
|
|
64
|
+
#stalledAt = 0;
|
|
65
|
+
readonly #readTimeoutMs: number;
|
|
66
|
+
readonly #answerTimeoutMs: number;
|
|
57
67
|
|
|
58
68
|
constructor(options: { connector: CompanyConnector; events: GatewayEventStore; hubProject(cwd: string): string;
|
|
59
|
-
folder(projectRef: string): Promise<string | null
|
|
69
|
+
folder(projectRef: string): Promise<string | null>; readTimeoutMs?: number; answerTimeoutMs?: number }) {
|
|
60
70
|
this.#connector = options.connector; this.#events = options.events;
|
|
61
71
|
this.#hubProject = options.hubProject; this.#folder = options.folder;
|
|
72
|
+
this.#readTimeoutMs = options.readTimeoutMs ?? READ_TIMEOUT_MS; this.#answerTimeoutMs = options.answerTimeoutMs ?? ANSWER_TIMEOUT_MS;
|
|
62
73
|
}
|
|
63
74
|
|
|
64
75
|
status(): CompanyStatus {
|
|
@@ -91,9 +102,12 @@ export class CompanyRelay {
|
|
|
91
102
|
if (!socket) throw new Error("company-gateway-stopped");
|
|
92
103
|
this.#link = await this.#bootstrap(socket);
|
|
93
104
|
await this.#open(this.#link);
|
|
105
|
+
// Ready only once a read is answered: the reads of the dashboard wait on it.
|
|
106
|
+
await this.#answers(this.#link);
|
|
94
107
|
this.#state = "ready"; this.#fresh = false;
|
|
95
108
|
} catch (error) {
|
|
96
109
|
const reason = failure(error, "company-connect-failed");
|
|
110
|
+
if (reason === "company-gateway-timeout") this.#drop();
|
|
97
111
|
// Agent Watch re-imports after an update on its own, so a changed
|
|
98
112
|
// binding is not kept once a later attach finds nothing running.
|
|
99
113
|
this.#state = "unavailable";
|
|
@@ -128,7 +142,7 @@ export class CompanyRelay {
|
|
|
128
142
|
if (typeof launch !== "string") throw new Error("company-gateway-starting");
|
|
129
143
|
const url = new URL(launch), capability = new URLSearchParams(url.hash.slice(1)).get("bootstrap");
|
|
130
144
|
if (!LOOPBACK.test(url.origin) || !capability) throw new Error("company-gateway-invalid");
|
|
131
|
-
const response = await fetch(`${url.origin}/api/v1/bootstrap`, { method: "POST", redirect: "error",
|
|
145
|
+
const response = await fetch(`${url.origin}/api/v1/bootstrap`, { method: "POST", redirect: "error", signal: AbortSignal.timeout(this.#readTimeoutMs),
|
|
132
146
|
headers: { Origin: url.origin, "Content-Type": "application/json", Accept: "application/json" }, body: JSON.stringify({ capability }) });
|
|
133
147
|
const cookie = response.headers.get("set-cookie")?.split(";", 1)[0], body = await response.json().catch(() => null) as { csrfToken?: unknown } | null;
|
|
134
148
|
if (!response.ok || !cookie || typeof body?.csrfToken !== "string") throw new Error("company-gateway-auth-failed");
|
|
@@ -177,6 +191,33 @@ export class CompanyRelay {
|
|
|
177
191
|
});
|
|
178
192
|
}
|
|
179
193
|
|
|
194
|
+
// Lets go of a company Gateway that does not answer, without waiting for its socket to close.
|
|
195
|
+
#drop(): void {
|
|
196
|
+
this.#stalledAt = Date.now();
|
|
197
|
+
const ws = this.#ws; this.#ws = null; this.#link = null; ws?.terminate();
|
|
198
|
+
for (const [, pending] of this.#pending) { clearTimeout(pending.timer); pending.reject(new Error("company-gateway-timeout")); }
|
|
199
|
+
this.#pending.clear();
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
async #answers(link: Link): Promise<void> {
|
|
203
|
+
try {
|
|
204
|
+
const response = await fetch(`${link.origin}/api/v1/session-live-state`, { redirect: "error", signal: AbortSignal.timeout(this.#answerTimeoutMs),
|
|
205
|
+
headers: { Accept: "application/json", Origin: link.origin, Cookie: link.cookie } });
|
|
206
|
+
await response.arrayBuffer();
|
|
207
|
+
} catch (error) { throw timedOut(error) ? new Error("company-gateway-timeout") : error; }
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// A read that was not answered in time: the company rows are left out and
|
|
211
|
+
// the Gateway is asked again later, so the dashboard's own lists keep loading.
|
|
212
|
+
#stalled(): void {
|
|
213
|
+
this.#drop();
|
|
214
|
+
this.#state = "unavailable"; this.#reason = "company-gateway-timeout";
|
|
215
|
+
this.#events.publish("catalog.changed", { reasonCode: "company-unavailable" });
|
|
216
|
+
if (this.#closed) return;
|
|
217
|
+
if (this.#retry) clearTimeout(this.#retry);
|
|
218
|
+
this.#retry = setTimeout(() => void this.connect(false), STALL_RETRY_MS); this.#retry.unref();
|
|
219
|
+
}
|
|
220
|
+
|
|
180
221
|
// Reads only attach; starting the company runtime (and possibly asking for
|
|
181
222
|
// Keychain access) is left to commands and the explicit connect route.
|
|
182
223
|
async #ready(launch = false): Promise<Link> {
|
|
@@ -190,10 +231,18 @@ export class CompanyRelay {
|
|
|
190
231
|
async http(method: "GET" | "POST", path: string, body?: unknown): Promise<{ status: number; value: unknown }> {
|
|
191
232
|
for (let attempt = 0; ; attempt += 1) {
|
|
192
233
|
const link = await this.#ready();
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
234
|
+
let response: Response;
|
|
235
|
+
try {
|
|
236
|
+
response = await fetch(`${link.origin}${path}`, { method, redirect: "error",
|
|
237
|
+
signal: AbortSignal.timeout(method === "GET" ? this.#readTimeoutMs : CHANGE_TIMEOUT_MS),
|
|
238
|
+
headers: { Accept: "application/json", Origin: link.origin, Cookie: link.cookie,
|
|
239
|
+
...(method === "POST" ? { "Content-Type": "application/json", "X-Piagent-CSRF": link.csrf } : {}) },
|
|
240
|
+
body: method === "POST" ? JSON.stringify(body) : undefined });
|
|
241
|
+
} catch (error) {
|
|
242
|
+
if (!timedOut(error)) throw error;
|
|
243
|
+
if (method === "GET" && this.#link === link) this.#stalled();
|
|
244
|
+
throw new Error("company-gateway-timeout");
|
|
245
|
+
}
|
|
197
246
|
if (response.status === 401 && attempt === 0) { this.#link = await this.#bootstrap(link.socket); continue; }
|
|
198
247
|
// The browser's own session is not the one refused here.
|
|
199
248
|
if (response.status === 401 || response.status === 403) return { status: 503, value: { error: { code: "company-auth-failed" } } };
|
|
@@ -232,7 +281,7 @@ export class CompanyRelay {
|
|
|
232
281
|
#attachSoon(): void {
|
|
233
282
|
if (this.#closed || this.#connecting || this.#state === "ready" || !this.#connector.configured()) return;
|
|
234
283
|
const now = Date.now();
|
|
235
|
-
if (now - this.#lastAttach < 10_000) return;
|
|
284
|
+
if (now - this.#lastAttach < 10_000 || now - this.#stalledAt < STALL_RETRY_MS) return;
|
|
236
285
|
this.#lastAttach = now; void this.connect(false);
|
|
237
286
|
}
|
|
238
287
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { managedThinkingLevels } from '../../piagent-core/managed/capabilities.mjs';
|
|
1
|
+
import { managedThinkingLevels, nearestLevel } from '../../piagent-core/managed/capabilities.mjs';
|
|
2
2
|
import { ManagedBrokerClient } from '../../piagent-core/managed/broker-client.mjs';
|
|
3
3
|
import { ManagedSession } from '../../piagent-core/managed/session.mjs';
|
|
4
4
|
import fs from 'node:fs';
|
|
@@ -30,7 +30,9 @@ export async function startManagedGateway({ config, configPath, cwd, agentDir, p
|
|
|
30
30
|
try {
|
|
31
31
|
runtime=await ManagedSession.create({sdkRoot:config.sdk_root,cwd:info.cwd,origin:config.origin,broker,agentDir,
|
|
32
32
|
sessionManager:manager??host.SessionManager.open(info.path),protectedRoots:[configPath,agentDir],renewBroker:newBroker,bindingRevision});
|
|
33
|
-
|
|
33
|
+
// The page may still offer the levels of the Harness before an administrator
|
|
34
|
+
// changed it: the conversation takes the nearest level the Harness allows now.
|
|
35
|
+
if(initial) runtime.session.setThinkingLevel(nearestLevel(initial.thinkingLevel,managedThinkingLevels(runtime.manifest,runtime.modelRuntime,runtime.session.model)));
|
|
34
36
|
await runtime.session.bindExtensions({mode:'rpc',uiContext:rpcUiContext()});
|
|
35
37
|
return {session:runtime.session,dispose:()=>runtime.dispose()};
|
|
36
38
|
} catch(error) { if(runtime) await runtime.dispose(); else await broker.dispose(); throw error; }
|
|
@@ -26,7 +26,9 @@ export function managedProjection(context: any, entries: any[]) {
|
|
|
26
26
|
const maximum = Number.isInteger(counter?.maximum) && counter.maximum >= 0 && counter.maximum <= 4 ? counter.maximum : 2;
|
|
27
27
|
// The member's access choice: "trusted-full-access" is Bypass.
|
|
28
28
|
const permission = last('agent-watch-permission')?.mode === 'trusted-full-access' ? 'trusted-full-access' : 'workspace-write';
|
|
29
|
-
|
|
29
|
+
// Fast mode, only where the conversation's Harness allows it (fast-mode.mjs).
|
|
30
|
+
const fast = last('agent-watch-fast');
|
|
31
|
+
return {modelLabel:'agent-watch-auto', managedPermission: permission, ...(fast?.allowed === true ? {managedFast: fast.enabled === true ? 'on' as const : 'off' as const} : {}), managedHelpers:{active:Number.isInteger(counter?.active) && counter.active>=0 && counter.active<=maximum ? counter.active : 0, maximum},
|
|
30
32
|
...(Array.isArray(capabilities)?{managedThinkingLevels:levels.filter(level=>capabilities.includes(level))}:{}),
|
|
31
33
|
...(steps ? {managedPlan:{steps}} : {}), ...(process ? {managedProcess:process} : {}), ...(turnEnd ? {managedTurnEnd:turnEnd} : {})};
|
|
32
34
|
}
|
|
@@ -32,6 +32,15 @@ export async function executePermissionCommand(session: any,
|
|
|
32
32
|
if (!profile || profile.mode !== permissionMode || profile.warning) throw new Error("session-permission-unavailable");
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
+
// Fast mode of a company conversation, where its Harness allows it. Like the
|
|
36
|
+
// permission, the runtime reads it at each request, so it may change mid-turn.
|
|
37
|
+
// True when the conversation's setting changed.
|
|
38
|
+
export function executeFastCommand(session: any, fast: boolean): boolean {
|
|
39
|
+
if (!session) throw new Error("session-runtime-unavailable");
|
|
40
|
+
if (!session.managedExecution || typeof session.managedSetFast !== "function") throw new Error("managed-fast-unavailable");
|
|
41
|
+
return session.managedSetFast(fast) === true;
|
|
42
|
+
}
|
|
43
|
+
|
|
35
44
|
export async function executeRuntimeCommand(session: any, command: string): Promise<{
|
|
36
45
|
outputs: RuntimeCommandOutput[];
|
|
37
46
|
modelCallObserved: boolean;
|
|
@@ -49,7 +49,7 @@ function catalogRef(key: Buffer, namespace: string, value: string): string {
|
|
|
49
49
|
}
|
|
50
50
|
|
|
51
51
|
export type SessionOwnerProjection = Pick<SessionRow, "state" | "liveState" | "composerAvailable" | "needsAttention" | "owner" | "reasonCode">;
|
|
52
|
-
export type SessionOptionProjection = Pick<SessionRow, "modelLabel" | "thinkingLevel" | "managedHelpers" | "managedThinkingLevels" | "managedPermission" | "managedPlan" | "managedProcess" | "managedTurnEnd">;
|
|
52
|
+
export type SessionOptionProjection = Pick<SessionRow, "modelLabel" | "thinkingLevel" | "managedHelpers" | "managedThinkingLevels" | "managedPermission" | "managedFast" | "managedPlan" | "managedProcess" | "managedTurnEnd">;
|
|
53
53
|
|
|
54
54
|
function display(value: unknown, maximum: number, fallback: string): string {
|
|
55
55
|
const clean = redactSensitiveText(String(value ?? "")).text
|
|
@@ -135,6 +135,7 @@ function row(key: Buffer, info: PiSessionInfo, metadata: SessionMetadata | undef
|
|
|
135
135
|
active: ownership?.liveState === 'idle' ? 0 : ownership?.liveState === 'running' || ownership?.liveState === 'waiting-approval' ? sessionOptions.managedHelpers.active : null}} : {}),
|
|
136
136
|
...(sessionOptions?.managedThinkingLevels ? {managedThinkingLevels:sessionOptions.managedThinkingLevels} : {}),
|
|
137
137
|
...(sessionOptions?.managedPermission ? {managedPermission:sessionOptions.managedPermission} : {}),
|
|
138
|
+
...(sessionOptions?.managedFast ? {managedFast:sessionOptions.managedFast} : {}),
|
|
138
139
|
...(sessionOptions?.managedPlan ? {managedPlan:sessionOptions.managedPlan} : {}),
|
|
139
140
|
...(sessionOptions?.managedProcess ? {managedProcess:sessionOptions.managedProcess} : {}),
|
|
140
141
|
...(sessionOptions?.managedTurnEnd ? {managedTurnEnd:sessionOptions.managedTurnEnd} : {}),
|
|
@@ -146,7 +147,7 @@ function row(key: Buffer, info: PiSessionInfo, metadata: SessionMetadata | undef
|
|
|
146
147
|
? { kind: "none", ownerEpoch: null, gatewayInstanceRef: null, runtimeInstanceRef: null, continuity: "released" }
|
|
147
148
|
: ownership?.owner ?? { kind: "none", ownerEpoch: null, gatewayInstanceRef: null, runtimeInstanceRef: null, continuity: "unknown" },
|
|
148
149
|
sessionRevision: revision(key, [info.path, info.modified.toISOString(), info.messageCount, info.name ?? null, metadata?.revision ?? null,
|
|
149
|
-
sessionOptions?.modelLabel ?? null, sessionOptions?.thinkingLevel ?? "unknown", sessionOptions?.managedHelpers, sessionOptions?.managedThinkingLevels, sessionOptions?.managedPermission ?? null, sessionOptions?.managedPlan, sessionOptions?.managedProcess, sessionOptions?.managedTurnEnd,
|
|
150
|
+
sessionOptions?.modelLabel ?? null, sessionOptions?.thinkingLevel ?? "unknown", sessionOptions?.managedHelpers, sessionOptions?.managedThinkingLevels, sessionOptions?.managedPermission ?? null, sessionOptions?.managedFast ?? null, sessionOptions?.managedPlan, sessionOptions?.managedProcess, sessionOptions?.managedTurnEnd,
|
|
150
151
|
gitBranch, gitRepositories,
|
|
151
152
|
metadata?.archived ? "archived" : ownership ? [ownership.state, ownership.liveState, ownership.owner.kind,
|
|
152
153
|
ownership.owner.ownerEpoch, ownership.owner.runtimeInstanceRef, ownership.reasonCode] : "offline"]),
|
|
@@ -13,7 +13,7 @@ import { SessionMetadataStore } from "./session-metadata-store.ts";
|
|
|
13
13
|
const REF = /^[A-Za-z0-9][A-Za-z0-9._~-]{0,159}$/;
|
|
14
14
|
const REVISION = /^[A-Za-z0-9][A-Za-z0-9._:~-]{0,159}$/;
|
|
15
15
|
const IDEMPOTENCY = /^[A-Za-z0-9_-]{22,96}$/;
|
|
16
|
-
const ACTIONS: SessionAction[] = ["session.create", "session.send", "session.abort", "session.set-model", "session.set-thinking", "session.set-permission", "session.rename", "session.pin", "session.archive",
|
|
16
|
+
const ACTIONS: SessionAction[] = ["session.create", "session.send", "session.abort", "session.set-model", "session.set-thinking", "session.set-permission", "session.set-fast", "session.rename", "session.pin", "session.archive",
|
|
17
17
|
"session.unarchive", "session.fork", "session.acquire", "session.release"];
|
|
18
18
|
|
|
19
19
|
type SessionCommand = SessionCommandIdentity & {
|
|
@@ -58,6 +58,7 @@ function validPayload(action: SessionAction, payload: Record<string, unknown>):
|
|
|
58
58
|
&& ["off", "minimal", "low", "medium", "high", "xhigh", "max"].includes(String(payload.thinkingLevel));
|
|
59
59
|
if (action === "session.set-permission") return exactKeys(payload, ["permissionMode"])
|
|
60
60
|
&& ["read-only", "workspace-write", "trusted-full-access"].includes(String(payload.permissionMode));
|
|
61
|
+
if (action === "session.set-fast") return exactKeys(payload, ["fast"]) && typeof payload.fast === "boolean";
|
|
61
62
|
if (action === "session.rename") return exactKeys(payload, ["title"]) && typeof payload.title === "string"
|
|
62
63
|
&& payload.title.length >= 1 && payload.title.length <= 500 && !/[\u0000-\u0008\u000b\u000c\u000e-\u001f\u007f]/.test(payload.title);
|
|
63
64
|
if (action === "session.pin") return exactKeys(payload, ["pinned"]) && typeof payload.pinned === "boolean";
|
|
@@ -160,7 +161,7 @@ export class SessionCommandController {
|
|
|
160
161
|
let deferredDispatch: Pick<SessionSendResult, "dispatch" | "cancel"> | null = null;
|
|
161
162
|
let observedCatalog = current, observedRow = row;
|
|
162
163
|
try {
|
|
163
|
-
let resultCode: "created" | "acquired" | "released" | "started" | "queued" | "steered" | "aborted" | "model-changed" | "thinking-changed" | "permission-changed"
|
|
164
|
+
let resultCode: "created" | "acquired" | "released" | "started" | "queued" | "steered" | "aborted" | "model-changed" | "thinking-changed" | "fast-changed" | "permission-changed"
|
|
164
165
|
| "renamed" | "pinned" | "unpinned" | "archived" | "unarchived" | "forked" | "no-change",
|
|
165
166
|
operationRef: string | null = null;
|
|
166
167
|
if (command.action === "session.create") {
|
|
@@ -228,6 +229,8 @@ export class SessionCommandController {
|
|
|
228
229
|
} else if (command.action === "session.set-permission") {
|
|
229
230
|
resultCode = await this.#runtimes.setPermission(command.sessionRef!, String(command.payload.permissionMode) as
|
|
230
231
|
"read-only" | "workspace-write" | "trusted-full-access");
|
|
232
|
+
} else if (command.action === "session.set-fast") {
|
|
233
|
+
resultCode = await this.#runtimes.setFast(command.sessionRef!, command.payload.fast === true);
|
|
231
234
|
} else if (command.action === "session.rename") {
|
|
232
235
|
resultCode = await this.#runtimes.rename(command.sessionRef!, String(command.payload.title).trim());
|
|
233
236
|
} else if (command.action === "session.fork") {
|
|
@@ -320,7 +323,7 @@ export class SessionCommandController {
|
|
|
320
323
|
sessionRevisionAfter: row?.sessionRevision ?? null, deduplicated: false };
|
|
321
324
|
}
|
|
322
325
|
#settled(command: SessionCommand, catalog: Catalog, row: SessionRow,
|
|
323
|
-
resultCode: "created" | "acquired" | "released" | "started" | "queued" | "steered" | "aborted" | "model-changed" | "thinking-changed" | "permission-changed"
|
|
326
|
+
resultCode: "created" | "acquired" | "released" | "started" | "queued" | "steered" | "aborted" | "model-changed" | "thinking-changed" | "fast-changed" | "permission-changed"
|
|
324
327
|
| "renamed" | "pinned" | "unpinned" | "archived" | "unarchived" | "forked" | "no-change",
|
|
325
328
|
operationRef: string | null): Receipt {
|
|
326
329
|
return { ...this.#base(command, catalog, row), operationRef, phase: "settled", resultCode, settledAt: this.#now().toISOString(),
|
|
@@ -11,7 +11,7 @@ const REF = /^[A-Za-z0-9][A-Za-z0-9._~-]{0,159}$/;
|
|
|
11
11
|
const REVISION = /^[A-Za-z0-9][A-Za-z0-9._:~-]{0,159}$/;
|
|
12
12
|
const REASON = /^[a-z0-9][a-z0-9.-]{0,95}$/;
|
|
13
13
|
const DIGEST = /^sha256:[a-f0-9]{64}$/;
|
|
14
|
-
const ACTIONS = ["session.create", "session.send", "session.abort", "session.set-model", "session.set-thinking", "session.set-permission", "session.rename", "session.pin", "session.archive",
|
|
14
|
+
const ACTIONS = ["session.create", "session.send", "session.abort", "session.set-model", "session.set-thinking", "session.set-permission", "session.set-fast", "session.rename", "session.pin", "session.archive",
|
|
15
15
|
"session.unarchive", "session.fork", "session.acquire", "session.release"] as const;
|
|
16
16
|
|
|
17
17
|
export type SessionAction = typeof ACTIONS[number];
|
|
@@ -72,14 +72,14 @@ export function validSessionReceipt(value: unknown): value is Receipt {
|
|
|
72
72
|
const settled = exactTimestamp(receipt.settledAt), evidence = typeof receipt.evidenceRef === "string" && REF.test(receipt.evidenceRef);
|
|
73
73
|
if (receipt.phase === "accepted") return receipt.resultCode === "accepted" && receipt.settledAt === null && receipt.evidenceRef === null && receipt.error === null;
|
|
74
74
|
if (receipt.phase === "settled") {
|
|
75
|
-
const results = ["created", "started", "queued", "steered", "aborted", "model-changed", "thinking-changed", "permission-changed", "renamed", "pinned", "unpinned", "archived", "unarchived",
|
|
75
|
+
const results = ["created", "started", "queued", "steered", "aborted", "model-changed", "thinking-changed", "permission-changed", "fast-changed", "renamed", "pinned", "unpinned", "archived", "unarchived",
|
|
76
76
|
"forked", "acquired", "released", "no-change"];
|
|
77
77
|
const actionResults: Partial<Record<SessionAction, string[]>> = {
|
|
78
78
|
"session.create": ["created", "started"],
|
|
79
79
|
"session.acquire": ["acquired", "no-change"], "session.release": ["released", "no-change"],
|
|
80
80
|
"session.send": ["started", "queued", "steered"], "session.abort": ["aborted", "no-change"],
|
|
81
81
|
"session.set-model": ["model-changed", "no-change"], "session.set-thinking": ["thinking-changed", "no-change"],
|
|
82
|
-
"session.set-permission": ["permission-changed", "no-change"], "session.rename": ["renamed", "no-change"],
|
|
82
|
+
"session.set-permission": ["permission-changed", "no-change"], "session.set-fast": ["fast-changed", "no-change"], "session.rename": ["renamed", "no-change"],
|
|
83
83
|
"session.pin": ["pinned", "unpinned", "no-change"], "session.archive": ["archived", "no-change"],
|
|
84
84
|
"session.unarchive": ["unarchived", "no-change"], "session.fork": ["forked"]
|
|
85
85
|
};
|
|
@@ -34,7 +34,12 @@ export function inspectEffectiveSessionOptions(session: any, modelRef: string |
|
|
|
34
34
|
thinkingLevel: null, reasonCode: "session-thinking-effect-unknown" };
|
|
35
35
|
if (modelRef && effectiveModel !== modelRef) return { state: "mismatch", modelRef: effectiveModel,
|
|
36
36
|
thinkingLevel: effectiveThinking, reasonCode: "session-model-mismatch" };
|
|
37
|
-
|
|
37
|
+
// A company conversation thinks at a level its Harness allows. The new chat
|
|
38
|
+
// page can still offer the levels of the Harness before an administrator
|
|
39
|
+
// changed it (high, when the Harness now fixes max): the runtime takes the
|
|
40
|
+
// nearest allowed level, and that is the conversation's level, not a create
|
|
41
|
+
// that failed. Refusing it left the message unsent under a "resyncing" notice.
|
|
42
|
+
if (effectiveThinking !== thinkingLevel && session?.managedExecution !== true) return { state: "mismatch", modelRef: effectiveModel,
|
|
38
43
|
thinkingLevel: effectiveThinking, reasonCode: "session-thinking-mismatch" };
|
|
39
44
|
return { state: "confirmed", modelRef: effectiveModel, thinkingLevel: effectiveThinking, reasonCode: null };
|
|
40
45
|
}
|
|
@@ -11,7 +11,7 @@ import { isUserConversationSession, projectRefForCwd, sessionRefForPath, type Se
|
|
|
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 { executePermissionCommand, executeRuntimeCommand } from "./runtime-session-controls.ts";
|
|
14
|
+
import { executeFastCommand, executePermissionCommand, executeRuntimeCommand } from "./runtime-session-controls.ts";
|
|
15
15
|
import { inspectEffectiveSessionOptions, effectiveModelRef, effectiveThinkingLevel, type EffectiveSessionOptions } from "./session-effective-options.ts";
|
|
16
16
|
import { launchSessionPrompt, type LaunchedSessionPrompt, type SessionSendResult } from "./session-prompt-dispatch.ts";
|
|
17
17
|
import { WEBUI_MESSAGE_CORRELATION_ENTRY_TYPE } from "../shared/message-correlation.ts";
|
|
@@ -348,22 +348,19 @@ export class SessionRuntimeSupervisor {
|
|
|
348
348
|
const effective = effectiveModelRef(session);
|
|
349
349
|
if (!effective) throw new Error("session-model-effect-unknown");
|
|
350
350
|
if (effective !== modelRef) throw new Error("session-model-mismatch");
|
|
351
|
-
this.#touchCreated(sessionRef);
|
|
352
|
-
return "model-changed";
|
|
351
|
+
this.#touchCreated(sessionRef); return "model-changed";
|
|
353
352
|
}
|
|
354
353
|
async setThinking(sessionRef: string, thinkingLevel: string): Promise<"thinking-changed" | "no-change"> {
|
|
355
354
|
await this.acquire(sessionRef);
|
|
356
355
|
const active = this.#active.get(sessionRef), session = active?.runtime.session;
|
|
357
356
|
if (!active || !session) throw new Error("session-runtime-unavailable");
|
|
358
357
|
if (active.operationRef || !session.isIdle) throw new Error("session-runtime-busy");
|
|
359
|
-
|
|
360
|
-
if (current === thinkingLevel) return "no-change";
|
|
358
|
+
if (effectiveThinkingLevel(session) === thinkingLevel) return "no-change";
|
|
361
359
|
await session.setThinkingLevel(thinkingLevel);
|
|
362
360
|
const effective = effectiveThinkingLevel(session);
|
|
363
361
|
if (!effective) throw new Error("session-thinking-effect-unknown");
|
|
364
362
|
if (effective !== thinkingLevel) throw new Error("session-thinking-mismatch");
|
|
365
|
-
this.#touchCreated(sessionRef);
|
|
366
|
-
return "thinking-changed";
|
|
363
|
+
this.#touchCreated(sessionRef); return "thinking-changed";
|
|
367
364
|
}
|
|
368
365
|
async setPermission(sessionRef: string, permissionMode: "read-only" | "workspace-write" | "trusted-full-access"): Promise<"permission-changed"> {
|
|
369
366
|
await this.acquire(sessionRef);
|
|
@@ -371,8 +368,11 @@ export class SessionRuntimeSupervisor {
|
|
|
371
368
|
if (!active || !session) throw new Error("session-runtime-unavailable");
|
|
372
369
|
if (!session.managedExecution && (active.operationRef || !session.isIdle)) throw new Error("session-runtime-busy");
|
|
373
370
|
await executePermissionCommand(session, permissionMode);
|
|
374
|
-
this.#touchCreated(sessionRef);
|
|
375
|
-
|
|
371
|
+
this.#touchCreated(sessionRef); return "permission-changed";
|
|
372
|
+
}
|
|
373
|
+
async setFast(sessionRef: string, fast: boolean): Promise<"fast-changed" | "no-change"> {
|
|
374
|
+
await this.acquire(sessionRef); const changed = executeFastCommand(this.#active.get(sessionRef)?.runtime.session, fast);
|
|
375
|
+
this.#touchCreated(sessionRef); return changed ? "fast-changed" : "no-change";
|
|
376
376
|
}
|
|
377
377
|
async runRuntimeCommand(sessionRef: string, command: string): Promise<RuntimeCommandResult> {
|
|
378
378
|
await this.acquire(sessionRef);
|
|
@@ -14,7 +14,8 @@ export const LAUNCH_REASONS: Record<string, [string, string]> = {
|
|
|
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
15
|
"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
16
|
"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
|
-
"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."]
|
|
17
|
+
"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."],
|
|
18
|
+
"company-gateway-timeout": ["Phiên công ty đang chạy nhưng không trả lời. Đóng Piagent, chạy `pkill -f piagent-studio.mjs` trong Terminal (Windows: trong Ubuntu), rồi mở lại.", "The company runtime is running but does not answer. Close Piagent, run `pkill -f piagent-studio.mjs` in a terminal (Windows: in Ubuntu), then open it again."]
|
|
18
19
|
};
|
|
19
20
|
|
|
20
21
|
export type Copy = { title: [string, string]; text: [string, string] };
|
|
@@ -92,7 +92,7 @@
|
|
|
92
92
|
"composerAvailable": { "type": "boolean" },
|
|
93
93
|
"needsAttention": { "type": "boolean" },
|
|
94
94
|
"managedHelpers": { "type": "object", "additionalProperties": false, "required": ["active", "maximum"], "properties": {"active": {"type": ["integer", "null"], "minimum": 0, "maximum": 4}, "maximum": {"type": "integer", "minimum": 0, "maximum": 4}} },
|
|
95
|
-
"managedThinkingLevels": {"type": "array", "uniqueItems": true, "maxItems": 7, "items": {"enum": ["off","minimal","low","medium","high","xhigh","max"]}}, "managedPermission": {"enum": ["workspace-write","trusted-full-access"]},
|
|
95
|
+
"managedThinkingLevels": {"type": "array", "uniqueItems": true, "maxItems": 7, "items": {"enum": ["off","minimal","low","medium","high","xhigh","max"]}}, "managedPermission": {"enum": ["workspace-write","trusted-full-access"]}, "managedFast": {"enum": ["off","on"]},
|
|
96
96
|
"managedPlan": { "type": "object", "additionalProperties": false, "required": ["steps"], "properties": {
|
|
97
97
|
"steps": { "type": "array", "minItems": 1, "maxItems": 30, "items": { "type": "object", "additionalProperties": false, "required": ["step", "status"], "properties": {
|
|
98
98
|
"step": { "type": "string", "minLength": 1, "maxLength": 200 }, "status": { "enum": ["pending", "in_progress", "completed", "deferred"] } } } } } },
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
"messageType": { "const": "command" },
|
|
30
30
|
"commandId": { "$ref": "common-v1.schema.json#/$defs/opaqueRef" },
|
|
31
31
|
"idempotencyKey": { "$ref": "#/$defs/idempotencyKey" },
|
|
32
|
-
"action": { "enum": ["session.create", "session.send", "session.abort", "session.set-model", "session.set-thinking", "session.set-permission", "session.rename", "session.pin", "session.archive", "session.unarchive", "session.fork", "session.acquire", "session.release"] },
|
|
32
|
+
"action": { "enum": ["session.create", "session.send", "session.abort", "session.set-model", "session.set-thinking", "session.set-permission", "session.set-fast", "session.rename", "session.pin", "session.archive", "session.unarchive", "session.fork", "session.acquire", "session.release"] },
|
|
33
33
|
"requestedAt": { "$ref": "common-v1.schema.json#/$defs/timestamp" },
|
|
34
34
|
"expiresAt": { "$ref": "common-v1.schema.json#/$defs/timestamp" },
|
|
35
35
|
"sessionRef": { "$ref": "common-v1.schema.json#/$defs/nullableOpaqueRef" },
|
|
@@ -144,7 +144,7 @@
|
|
|
144
144
|
{
|
|
145
145
|
"type": "object",
|
|
146
146
|
"properties": {
|
|
147
|
-
"action": { "enum": ["session.set-model", "session.set-thinking", "session.set-permission"] },
|
|
147
|
+
"action": { "enum": ["session.set-model", "session.set-thinking", "session.set-permission", "session.set-fast"] },
|
|
148
148
|
"sessionRef": { "$ref": "common-v1.schema.json#/$defs/opaqueRef" },
|
|
149
149
|
"expectedSessionRevision": { "$ref": "common-v1.schema.json#/$defs/revisionToken" },
|
|
150
150
|
"payload": {
|
|
@@ -166,6 +166,12 @@
|
|
|
166
166
|
"additionalProperties": false,
|
|
167
167
|
"required": ["permissionMode"],
|
|
168
168
|
"properties": { "permissionMode": { "$ref": "#/$defs/permissionMode" } }
|
|
169
|
+
},
|
|
170
|
+
{
|
|
171
|
+
"type": "object",
|
|
172
|
+
"additionalProperties": false,
|
|
173
|
+
"required": ["fast"],
|
|
174
|
+
"properties": { "fast": { "type": "boolean" } }
|
|
169
175
|
}
|
|
170
176
|
]
|
|
171
177
|
}
|
|
@@ -185,6 +191,11 @@
|
|
|
185
191
|
"if": { "properties": { "action": { "const": "session.set-permission" } }, "required": ["action"] },
|
|
186
192
|
"then": { "properties": { "payload": { "type": "object", "required": ["permissionMode"],
|
|
187
193
|
"properties": { "permissionMode": { "$ref": "#/$defs/permissionMode" } } } } }
|
|
194
|
+
},
|
|
195
|
+
{
|
|
196
|
+
"if": { "properties": { "action": { "const": "session.set-fast" } }, "required": ["action"] },
|
|
197
|
+
"then": { "properties": { "payload": { "type": "object", "required": ["fast"],
|
|
198
|
+
"properties": { "fast": { "type": "boolean" } } } } }
|
|
188
199
|
}
|
|
189
200
|
]
|
|
190
201
|
}
|
|
@@ -290,9 +301,9 @@
|
|
|
290
301
|
"messageType": { "const": "receipt" },
|
|
291
302
|
"commandId": { "$ref": "common-v1.schema.json#/$defs/opaqueRef" },
|
|
292
303
|
"idempotencyKeyDigest": { "$ref": "common-v1.schema.json#/$defs/digest" },
|
|
293
|
-
"action": { "enum": ["session.create", "session.send", "session.abort", "session.set-model", "session.set-thinking", "session.set-permission", "session.rename", "session.pin", "session.archive", "session.unarchive", "session.fork", "session.acquire", "session.release"] },
|
|
304
|
+
"action": { "enum": ["session.create", "session.send", "session.abort", "session.set-model", "session.set-thinking", "session.set-permission", "session.set-fast", "session.rename", "session.pin", "session.archive", "session.unarchive", "session.fork", "session.acquire", "session.release"] },
|
|
294
305
|
"phase": { "enum": ["accepted", "settled", "uncertain", "rejected"] },
|
|
295
|
-
"resultCode": { "enum": ["accepted", "created", "started", "queued", "steered", "aborted", "model-changed", "thinking-changed", "permission-changed", "renamed", "pinned", "unpinned", "archived", "unarchived", "forked", "acquired", "released", "no-change", "stale-revision", "owner-conflict", "recovery-required", "invalid-command", "expired", "unavailable", "effect-unknown"] },
|
|
306
|
+
"resultCode": { "enum": ["accepted", "created", "started", "queued", "steered", "aborted", "model-changed", "thinking-changed", "permission-changed", "fast-changed", "renamed", "pinned", "unpinned", "archived", "unarchived", "forked", "acquired", "released", "no-change", "stale-revision", "owner-conflict", "recovery-required", "invalid-command", "expired", "unavailable", "effect-unknown"] },
|
|
296
307
|
"requestedAt": { "$ref": "common-v1.schema.json#/$defs/timestamp" },
|
|
297
308
|
"settledAt": { "anyOf": [{ "$ref": "common-v1.schema.json#/$defs/timestamp" }, { "type": "null" }] },
|
|
298
309
|
"sessionRef": { "$ref": "common-v1.schema.json#/$defs/nullableOpaqueRef" },
|
|
@@ -325,7 +336,7 @@
|
|
|
325
336
|
"if": { "properties": { "phase": { "const": "settled" } }, "required": ["phase"] },
|
|
326
337
|
"then": {
|
|
327
338
|
"properties": {
|
|
328
|
-
"resultCode": { "enum": ["created", "started", "queued", "steered", "aborted", "model-changed", "thinking-changed", "permission-changed", "renamed", "pinned", "unpinned", "archived", "unarchived", "forked", "acquired", "released", "no-change"] },
|
|
339
|
+
"resultCode": { "enum": ["created", "started", "queued", "steered", "aborted", "model-changed", "thinking-changed", "permission-changed", "fast-changed", "renamed", "pinned", "unpinned", "archived", "unarchived", "forked", "acquired", "released", "no-change"] },
|
|
329
340
|
"settledAt": { "$ref": "common-v1.schema.json#/$defs/timestamp" },
|
|
330
341
|
"evidenceRef": { "$ref": "common-v1.schema.json#/$defs/opaqueRef" },
|
|
331
342
|
"error": { "type": "null" }
|
|
@@ -367,6 +378,10 @@
|
|
|
367
378
|
"if": { "properties": { "action": { "const": "session.set-permission" }, "phase": { "const": "settled" } }, "required": ["action", "phase"] },
|
|
368
379
|
"then": { "properties": { "resultCode": { "enum": ["permission-changed", "no-change"] } } }
|
|
369
380
|
},
|
|
381
|
+
{
|
|
382
|
+
"if": { "properties": { "action": { "const": "session.set-fast" }, "phase": { "const": "settled" } }, "required": ["action", "phase"] },
|
|
383
|
+
"then": { "properties": { "resultCode": { "enum": ["fast-changed", "no-change"] } } }
|
|
384
|
+
},
|
|
370
385
|
{
|
|
371
386
|
"if": { "properties": { "action": { "const": "session.send" }, "phase": { "const": "settled" } }, "required": ["action", "phase"] },
|
|
372
387
|
"then": { "properties": { "resultCode": { "enum": ["started", "queued", "steered"] }, "operationRef": { "$ref": "common-v1.schema.json#/$defs/opaqueRef" } } }
|
|
@@ -83,6 +83,28 @@ export function companyTerminalExtension({ managed, packageRoot, sdkRoot, bindin
|
|
|
83
83
|
ctx.ui.notify(PERMISSION_TEXT[session.permission] ?? PERMISSION_TEXT['workspace-write'], 'info');
|
|
84
84
|
} });
|
|
85
85
|
|
|
86
|
+
// Fast mode, where the team's Harness allows it; off until the member turns it on.
|
|
87
|
+
const showFast = (ctx) => ctx.ui?.setStatus?.('agent-watch-fast', managed()?.session?.managedFastState?.() === 'on' ? 'Fast' : undefined);
|
|
88
|
+
pi.on('session_start', async (_event, ctx) => showFast(ctx));
|
|
89
|
+
pi.registerCommand('fast', {
|
|
90
|
+
description: 'Bật/tắt Fast cho cuộc trò chuyện công ty này (on, off, status); chỉ có khi Harness của team cho phép',
|
|
91
|
+
getArgumentCompletions: (prefix) => ['on', 'off', 'status'].filter((v) => v.startsWith(prefix)).map((value) => ({ value, label: value })),
|
|
92
|
+
handler: async (args, ctx) => {
|
|
93
|
+
const session = managed()?.session, state = session?.managedFastState?.();
|
|
94
|
+
if (typeof session?.managedSetFast !== 'function') { ctx.ui.notify('Cuộc trò chuyện này chưa sẵn sàng.', 'error'); return; }
|
|
95
|
+
if (!state) { ctx.ui.notify('Harness của team không cho phép Fast. Admin bật trong Agent Studio → Harness → Fast mode.', 'warning'); return; }
|
|
96
|
+
const arg = args.trim().toLowerCase();
|
|
97
|
+
if (!['', 'on', 'off', 'status'].includes(arg)) { ctx.ui.notify('Cách dùng: /fast on|off|status', 'warning'); return; }
|
|
98
|
+
if (arg === 'on' || arg === 'off') session.managedSetFast(arg === 'on');
|
|
99
|
+
else if (arg === '') session.managedSetFast(state !== 'on');
|
|
100
|
+
showFast(ctx);
|
|
101
|
+
ctx.ui.notify(session.managedFastState() === 'on'
|
|
102
|
+
? session.managedFastServed?.() === false
|
|
103
|
+
? 'Fast đang bật nhưng model hiện tại của main agent không hỗ trợ Fast (chỉ các model GPT qua Codex): các lượt vẫn chạy tốc độ thường, quota tính như thường.'
|
|
104
|
+
: 'Fast đang bật: main agent trả lời nhanh hơn trên model GPT, token tính vào quota gấp 2,5 lần. Model và mức suy luận không đổi.'
|
|
105
|
+
: 'Fast đang tắt: các lượt chạy tốc độ thường.', 'info');
|
|
106
|
+
} });
|
|
107
|
+
|
|
86
108
|
// The Harness's notes carry English for the agent; the member reads them in Vietnamese.
|
|
87
109
|
pi.registerMessageRenderer('agent-watch-process', (message, _options, theme) => {
|
|
88
110
|
const lines = processLines(message.details);
|