Sync CopilotPortal agent files from Tools

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
vczh
2026-04-09 02:13:30 -07:00
parent d5c520e5c3
commit 0c79ce7d1f
6 changed files with 38 additions and 14 deletions
@@ -187,7 +187,7 @@ async function renderFlowChartMermaid(chart, container, onInspect) {
// Track currently selected TaskNode/CondNode
let currentSelectedGroup = null;
let currentSelectedOriginalStrokeWidth = null;
let currentSelectedOriginalStyle = null;
let currentSelectedWorkId = null;
// Map workId -> DOM group for status updates
@@ -196,7 +196,8 @@ async function renderFlowChartMermaid(chart, container, onInspect) {
// Add click handlers for TaskNode/CondNode elements
for (const nodeId of taskNodeIds) {
const nodeEl = container.querySelector(`[id^="flowchart-${nodeId}-"]`);
// Mermaid 10: id="flowchart-N0-123"; Mermaid 11: id="mermaid-chart-flowchart-N0-0"
const nodeEl = container.querySelector(`[id*="flowchart-${nodeId}-"]`);
if (!nodeEl) continue;
const group = nodeEl.closest("g.node") || nodeEl;
group.style.cursor = "pointer";
@@ -214,25 +215,26 @@ async function renderFlowChartMermaid(chart, container, onInspect) {
if (currentSelectedGroup === group) {
// Unselect
if (shapeEl && currentSelectedOriginalStrokeWidth !== null) {
shapeEl.style.strokeWidth = currentSelectedOriginalStrokeWidth;
if (shapeEl && currentSelectedOriginalStyle !== null) {
shapeEl.setAttribute("style", currentSelectedOriginalStyle);
}
currentSelectedGroup = null;
currentSelectedOriginalStrokeWidth = null;
currentSelectedOriginalStyle = null;
currentSelectedWorkId = null;
if (onInspect) onInspect(null);
} else {
// Unselect previous
if (currentSelectedGroup) {
const prevShape = currentSelectedGroup.querySelector("rect, polygon, circle, ellipse, path");
if (prevShape && currentSelectedOriginalStrokeWidth !== null) {
prevShape.style.strokeWidth = currentSelectedOriginalStrokeWidth;
if (prevShape && currentSelectedOriginalStyle !== null) {
prevShape.setAttribute("style", currentSelectedOriginalStyle);
}
}
// Select new
currentSelectedOriginalStrokeWidth = shapeEl ? (shapeEl.style.strokeWidth || shapeEl.getAttribute("style")?.match(/stroke-width:\s*([^;]+)/)?.[1] || getComputedStyle(shapeEl).strokeWidth) : null;
// Select new — save full style string so !important flags are preserved on restore
currentSelectedOriginalStyle = shapeEl ? (shapeEl.getAttribute("style") ?? "") : null;
if (shapeEl) {
shapeEl.style.strokeWidth = "5px";
// Use setProperty with "important" to override Mermaid 11's !important inline styles
shapeEl.style.setProperty("stroke-width", "5px", "important");
}
currentSelectedGroup = group;
currentSelectedWorkId = wid;
@@ -99,15 +99,16 @@ async function installJobsEntry(entryValue: Entry): Promise<void> {
}
const models = await helperGetModels();
const validModelIds = new Set(models.map(m => m.id));
const modelListText = models.map(m => ` ${m.name} (id: ${m.id}, multiplier: ${m.multiplier})`).join("\n");
for (const [category, modelId] of Object.entries(entryValue.models)) {
if (!validModelIds.has(modelId)) {
throw new Error(`entry.models["${category}"] refers to model "${modelId}" which is not a valid model.`);
throw new Error(`entry.models["${category}"] refers to model "${modelId}" which is not a valid model.\nAvailable models:\n${modelListText}`);
}
}
for (let i = 0; i < entryValue.drivingSessionRetries.length; i++) {
const modelId = entryValue.drivingSessionRetries[i].modelId;
if (!validModelIds.has(modelId)) {
throw new Error(`entry.drivingSessionRetries[${i}].modelId refers to model "${modelId}" which is not a valid model.`);
throw new Error(`entry.drivingSessionRetries[${i}].modelId refers to model "${modelId}" which is not a valid model.\nAvailable models:\n${modelListText}`);
}
}
installedEntry = entryValue;
@@ -50,7 +50,7 @@ const entryInput: Entry = {
models: {
driving: "gpt-5-mini",
planning: "gpt-5.2",
coding: "gpt-5.2-codex",
coding: "gpt-5.4",
reviewers1: "gpt-5.3-codex",
reviewers2: "claude-opus-4.6",
reviewers3: "claude-sonnet-4.6"
@@ -58,7 +58,7 @@ const entryInput: Entry = {
drivingSessionRetries: [
{ modelId: "gpt-5-mini", retries: 5 },
{ modelId: "gpt-4.1", retries: 5 },
{ modelId: "gpt-5.1-codex-mini", retries: 3 },
{ modelId: "gpt-5.4-mini", retries: 3 },
{ modelId: "claude-haiku-4.5", retries: 3 },
],
promptVariables: {
@@ -6,6 +6,25 @@ const __dirname = path.dirname(fileURLToPath(import.meta.url));
const serverScript = path.resolve(__dirname, "..", "dist", "index.js");
const windowsHidePatch = path.resolve(__dirname, "windowsHide.cjs");
// Stop any previously running server so the new one starts with clean state.
// Without this, a leftover server with an installed entry would cause false failures.
try {
await fetch("http://localhost:8888/api/stop");
} catch {
// No server running, that's fine
}
// Wait until the old server is gone (port free)
for (let i = 0; i < 20; i++) {
try {
await fetch("http://localhost:8888/api/test");
// Still responding — wait a bit more
await new Promise((r) => setTimeout(r, 200));
} catch {
// Connection refused: old server is gone
break;
}
}
// Spawn server as detached process in test mode so it runs independently of this script.
// On Windows, the Copilot SDK internally spawns node.exe to run its bundled CLI server.
// Without windowsHide, each spawn creates a visible console window that steals keyboard focus.
@@ -47,6 +47,7 @@ Prints the following URL for shortcut:
`async installJobsEntry(entry: Entry): Promise<void>;`
- Verify if all `entry.model[name]` is a valid model with `helperGetModels`.
- Verify if all `entry.drivingSessionRetries[index].modelId` is a valid model with `helperGetModels`.
- If a model is not valid, in the error messages a list of all model names, ids and premium multipliers should be listed as well.
- Use the entry. It could be `entry` from `jobsData.ts` or whatever.
- This function can only be called when no session is running, otherwise throws.
@@ -47,6 +47,7 @@ Prints the following URL for shortcut:
`async installJobsEntry(entry: Entry): Promise<void>;`
- Verify if all `entry.model[name]` is a valid model with `helperGetModels`.
- Verify if all `entry.drivingSessionRetries[index].modelId` is a valid model with `helperGetModels`.
- If a model is not valid, in the error messages a list of all model names, ids and premium multipliers should be listed as well.
- Use the entry. It could be `entry` from `jobsData.ts` or whatever.
- This function can only be called when no session is running, otherwise throws.