Update agent

This commit is contained in:
vczh
2026-03-02 04:51:08 -08:00
parent ce3cde6187
commit 00081d9611
15 changed files with 372 additions and 136 deletions

View File

@@ -138,18 +138,53 @@ async function renderFlowChartMermaid(chart, container, onInspect) {
const zoomMin = 0.2;
const zoomMax = 3;
const zoomStep = 0.1;
container.addEventListener("wheel", (e) => {
if (!e.ctrlKey) return;
e.preventDefault();
const delta = e.deltaY > 0 ? -zoomStep : zoomStep;
zoomLevel = Math.min(zoomMax, Math.max(zoomMin, zoomLevel + delta));
function applyZoom() {
const svgInner = container.querySelector("svg");
if (svgInner) {
svgInner.style.transformOrigin = "top left";
svgInner.style.transform = `scale(${zoomLevel})`;
}
}
container.addEventListener("wheel", (e) => {
if (!e.ctrlKey) return;
e.preventDefault();
const delta = e.deltaY > 0 ? -zoomStep : zoomStep;
zoomLevel = Math.min(zoomMax, Math.max(zoomMin, zoomLevel + delta));
applyZoom();
}, { passive: false });
// Touch pinch-to-zoom (two fingers)
let lastTouchDist = null;
container.addEventListener("touchstart", (e) => {
if (e.touches.length === 2) {
e.preventDefault();
const dx = e.touches[0].clientX - e.touches[1].clientX;
const dy = e.touches[0].clientY - e.touches[1].clientY;
lastTouchDist = Math.hypot(dx, dy);
}
}, { passive: false });
container.addEventListener("touchmove", (e) => {
if (e.touches.length === 2 && lastTouchDist !== null) {
e.preventDefault();
const dx = e.touches[0].clientX - e.touches[1].clientX;
const dy = e.touches[0].clientY - e.touches[1].clientY;
const newDist = Math.hypot(dx, dy);
const scale = newDist / lastTouchDist;
zoomLevel = Math.min(zoomMax, Math.max(zoomMin, zoomLevel * scale));
lastTouchDist = newDist;
applyZoom();
}
}, { passive: false });
container.addEventListener("touchend", (e) => {
if (e.touches.length < 2) {
lastTouchDist = null;
}
});
// Track currently selected TaskNode/CondNode
let currentSelectedGroup = null;
let currentSelectedOriginalStrokeWidth = null;

View File

@@ -172,3 +172,54 @@ html, body {
height: 100%;
overflow: auto;
}
/* ---- Phone-specific layout ---- */
/* Back button: hidden on desktop, visible only in phone mode */
.phone-back-btn {
display: none;
padding: 8px 16px;
border: 1px solid #444;
border-bottom: none;
border-radius: 4px 4px 0 0;
background: #dc2626;
color: #fff;
cursor: pointer;
font-size: 14px;
margin-left: auto;
flex-shrink: 0;
}
.phone-back-btn:hover {
background: #b91c1c;
}
@media (max-width: 768px) {
.phone-back-btn {
display: block;
}
#resize-bar {
display: none !important;
}
#right-part {
display: none !important;
position: fixed;
top: 0;
left: 0;
width: 100% !important;
height: 100%;
z-index: 1000;
background: #ffffff;
}
#right-part.phone-visible {
display: flex !important;
}
#left-part {
width: 100% !important;
flex: none;
}
}

View File

@@ -2,6 +2,7 @@
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Copilot Portal - Job Tracking</title>
<link rel="stylesheet" href="jobTracking.css">
<link rel="stylesheet" href="messageBlock.css">

View File

@@ -21,6 +21,7 @@ let chartController = null; // returned from renderFlowChartMermaid
let jobStatus = isPreviewMode ? "PREVIEW" : "RUNNING"; // PREVIEW | RUNNING | SUCCEEDED | FAILED | CANCELED
let jobStopped = false;
// Map: workId -> { taskId, sessions: Map<sessionId, { name, renderer, div, active }>, attemptCount }
const workIdData = {};
@@ -116,13 +117,6 @@ function showJsonView() {
function showTaskSessionTabs(workId) {
sessionResponsePart.innerHTML = "";
const data = workIdData[workId];
if (!data) {
sessionResponsePart.textContent = "No session data for this task.";
return;
}
// Reset active tab tracking so the first switchTabForWork always applies
data.activeTabSessionId = null;
tabContainer = document.createElement("div");
tabContainer.className = "tab-container";
@@ -137,6 +131,16 @@ function showTaskSessionTabs(workId) {
sessionResponsePart.appendChild(tabContainer);
if (!data || data.sessions.size === 0) {
// No session data yet — show placeholder in tab content
const placeholder = document.createElement("div");
placeholder.style.padding = "16px";
placeholder.textContent = "No session data for this task.";
tabContent.appendChild(placeholder);
} else {
// Reset active tab tracking so the first switchTabForWork always applies
data.activeTabSessionId = null;
// Ensure "Driving" tab always appears first
const sortedEntries = [...data.sessions.entries()].sort((a, b) => {
if (a[1].name === "Driving") return -1;
@@ -164,6 +168,19 @@ function showTaskSessionTabs(workId) {
if (firstEntry) {
switchTabForWork(workId, firstEntry[0]);
}
}
// Add "Back" button (visible only in phone mode via CSS)
const backBtn = document.createElement("button");
backBtn.className = "phone-back-btn";
backBtn.textContent = "Back";
backBtn.addEventListener("click", () => {
// Unselect the inspected node
inspectedWorkId = null;
rightPart.classList.remove("phone-visible");
showJsonView();
});
tabHeaders.appendChild(backBtn);
}
function refreshSessionResponsePart() {
@@ -177,6 +194,12 @@ function refreshSessionResponsePart() {
function onInspect(workId) {
inspectedWorkId = workId;
refreshSessionResponsePart();
if (workId !== null) {
// In phone mode CSS makes this a full-screen overlay; on desktop it has no effect
rightPart.classList.add("phone-visible");
} else {
rightPart.classList.remove("phone-visible");
}
}
// ---- Live Polling Helpers ----
@@ -317,6 +340,9 @@ function startTaskPolling(taskId, workId) {
div.style.display = "none";
tabContent.insertBefore(div, tabContent.firstChild);
}
// Ensure Back button stays at the end
const existingBackBtn = tabHeaders.querySelector(".phone-back-btn");
if (existingBackBtn) tabHeaders.appendChild(existingBackBtn);
}
}
@@ -349,7 +375,13 @@ function startTaskPolling(taskId, workId) {
tabBtn.addEventListener("click", () => {
switchTabForWork(workId, sessionId);
});
// Insert before phone Back button if present
const phoneBackBtn = tabHeaders.querySelector(".phone-back-btn");
if (phoneBackBtn) {
tabHeaders.insertBefore(tabBtn, phoneBackBtn);
} else {
tabHeaders.appendChild(tabBtn);
}
div.style.display = "none";
tabContent.appendChild(div);

View File

@@ -12,7 +12,7 @@
"testExecute": "node test/runTests.mjs"
},
"dependencies": {
"@github/copilot-sdk": "^0.1.4"
"@github/copilot-sdk": "^0.1.29"
},
"devDependencies": {
"@playwright/test": "^1.49.0",

View File

@@ -1,4 +1,4 @@
import { CopilotClient, defineTool, type CopilotSession } from "@github/copilot-sdk";
import { CopilotClient, defineTool, approveAll, type CopilotSession } from "@github/copilot-sdk";
export interface ICopilotSession {
get rawSection(): CopilotSession;
@@ -82,6 +82,7 @@ export async function startSession(
streaming: true,
workingDirectory,
tools: jobTools,
onPermissionRequest: approveAll,
hooks: {
onPreToolUse: async (input) => {
if (input.toolName === "glob") {

View File

@@ -17,6 +17,7 @@ import {
apiCopilotSessionQuery,
apiCopilotSessionLive,
hasRunningSessions,
helperGetModels,
} from "./copilotApi.js";
import {
apiTaskList,
@@ -96,6 +97,19 @@ async function installJobsEntry(entryValue: Entry): Promise<void> {
if (hasRunningSessions()) {
throw new Error("Cannot call installJobsEntry while sessions are running.");
}
const models = await helperGetModels();
const validModelIds = new Set(models.map(m => m.id));
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.`);
}
}
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.`);
}
}
installedEntry = entryValue;
}
@@ -308,12 +322,15 @@ const server = http.createServer((req, res) => {
serveStaticFile(res, filePath);
});
// Install the jobs entry (only if not in test mode)
if (!testMode) {
installJobsEntry(entry);
}
server.listen(port, () => {
// Install the jobs entry (only if not in test mode), then start server
async function startServer(): Promise<void> {
if (!testMode) {
await installJobsEntry(entry);
}
server.listen(port, () => {
console.log(`http://localhost:${port}`);
console.log(`http://localhost:${port}/api/stop`);
});
});
}
startServer();

View File

@@ -52,8 +52,8 @@ const entryInput: Entry = {
planning: "gpt-5.2",
coding: "gpt-5.2-codex",
reviewers1: "gpt-5.3-codex",
reviewers2: "claude-opus-4.5",
reviewers3: "gemini-3-pro-preview"
reviewers2: "claude-opus-4.6",
reviewers3: "claude-sonnet-4.6"
},
drivingSessionRetries: [
{ modelId: "gpt-5-mini", retries: 5 },
@@ -64,121 +64,123 @@ const entryInput: Entry = {
promptVariables: {
reviewerBoardFiles: [
"## Your Identity",
"You are $task-model, one of the reviewers in the review board.",
"- You are $task-model, one of the reviewers in the review board.",
"- The review document you are going to create is Copilot_Review_Writing_{YOUR-MODEL}.md",
"- You are going to review a target document, as well as any existing Copilot_Review_Finished_{OTHER-MODEL}.md.",
"## Reviewer Board Files",
"- gpt -> Copilot_Review_*_GPT.md",
"- claude opus -> Copilot_Review_*_OPUS.md",
"- gemini -> Copilot_Review_*_GEMINI.md",
"- claude sonnet -> Copilot_Review_*_SONNET.md",
],
copilotSdkTips: [
"NOTE: If you can't find the file, try different ways to make sure, including absolute path, relative path, powershell tool, view tool, slash and backslash, etc.",
"AVOID the glob tool to find any files, it does not work on Windows."
"- NOTE: If you can't find the file, try different ways to make sure, including absolute path, relative path, powershell tool, view tool, slash and backslash, etc.",
"- AVOID the glob tool to find any files, it does not work on Windows."
],
defineRepoRoot: [
"REPO-ROOT is the root directory of the repo (aka the working directory you are currently in)"
"- REPO-ROOT is the root directory of the repo (aka the working directory you are currently in)"
],
noQuestion: [
"DO NOT ask user if you can start doing something, especially after you made a plan, always perform your job automatically and proactively til the end."
"- DO NOT ask user if you can start doing something, especially after you made a plan, always perform your job automatically and proactively til the end."
],
cppjob: [
"$defineRepoRoot",
"$copilotSdkTips",
"YOU MUST FOLLOW REPO-ROOT/.github/copilot-instructions.md as a general guideline for all your tasks."
"- YOU MUST FOLLOW REPO-ROOT/.github/copilot-instructions.md as a general guideline for all your tasks."
],
scrum: [
"Execute the instruction in REPO-ROOT/.github/prompts/0-scrum.prompt.md immediately.",
"- Execute the instruction in REPO-ROOT/.github/prompts/0-scrum.prompt.md immediately.",
"$noQuestion"
],
design: [
"Execute the instruction in REPO-ROOT/.github/prompts/1-design.prompt.md immediately.",
"- Execute the instruction in REPO-ROOT/.github/prompts/1-design.prompt.md immediately.",
"$noQuestion"
],
plan: [
"Execute the instruction in REPO-ROOT/.github/prompts/2-planning.prompt.md immediately.",
"- Execute the instruction in REPO-ROOT/.github/prompts/2-planning.prompt.md immediately.",
"$noQuestion"
],
summary: [
"Execute the instruction in REPO-ROOT/.github/prompts/3-summarizing.prompt.md immediately.",
"- Execute the instruction in REPO-ROOT/.github/prompts/3-summarizing.prompt.md immediately.",
"$noQuestion"
],
codingPrefix: [
"**IMPORT**: It is FORBIDDEN to modify any script files in `REPO-ROOT/.github/Scripts`. If you are getting trouble, the only reason is your code has problem. Fix the code instead of any other kind of working around.",
"- **IMPORT**: It is FORBIDDEN to modify any script files in `REPO-ROOT/.github/Scripts`. If you are getting trouble, the only reason is your code has problem. Fix the code instead of any other kind of working around.",
],
execute: [
"$codingPrefix",
"Execute the instruction in REPO-ROOT/.github/prompts/4-execution.prompt.md immediately.",
"- Execute the instruction in REPO-ROOT/.github/prompts/4-execution.prompt.md immediately.",
"$noQuestion"
],
verify: [
"$codingPrefix",
"Execute the instruction in REPO-ROOT/.github/prompts/5-verifying.prompt.md immediately.",
"- Execute the instruction in REPO-ROOT/.github/prompts/5-verifying.prompt.md immediately.",
"$noQuestion"
],
refine: [
"Execute the instruction in REPO-ROOT/.github/prompts/refine.prompt.md immediately.",
"- Execute the instruction in REPO-ROOT/.github/prompts/refine.prompt.md immediately.",
"$noQuestion"
],
review: [
"Execute the instruction in REPO-ROOT/.github/prompts/review.prompt.md immediately.",
"- Execute the instruction in REPO-ROOT/.github/prompts/review.prompt.md immediately.",
"$noQuestion"
],
ask: [
"Execute the instruction in REPO-ROOT/.github/prompts/ask.prompt.md immediately.",
"- Execute the instruction in REPO-ROOT/.github/prompts/ask.prompt.md immediately.",
"$noQuestion"
],
code: [
"Execute the instruction in REPO-ROOT/.github/prompts/code.prompt.md immediately.",
"- Execute the instruction in REPO-ROOT/.github/prompts/code.prompt.md immediately.",
"$noQuestion"
],
reportDocument: [
"YOU MUST use the job_prepare_document tool with an argument: an absolute path of the document you are about to create or update.",
"YOU MUST use the job_prepare_document tool even when you think nothing needs to be updated, it is to make sure you are clear about which document to work on."
"- YOU MUST use the job_prepare_document tool with an argument: an absolute path of the document you are about to create or update.",
"- YOU MUST use the job_prepare_document tool even when you think nothing needs to be updated, it is to make sure you are clear about which document to work on."
],
reportBoolean: [
"YOU MUST use either job_boolean_true tool or job_boolean_false tool to answer an yes/no question, with the reason in the argument."
"- YOU MUST use either job_boolean_true tool or job_boolean_false tool to answer an yes/no question, with the reason in the argument."
],
simpleCondition: [
"$defineRepoRoot",
"$copilotSdkTips",
"$reportBoolean",
"Use job_boolean_true tool if the below condition satisfies, or use job_boolean_false tool if it does not satisfy."
"- Use job_boolean_true tool if the below condition satisfies, or use job_boolean_false tool if it does not satisfy."
],
scrumDocReady: [
"$simpleCondition",
"REPO-ROOT/.github/TaskLogs/Copilot_Scrum.md should exist and its content should not be just a title."
"- REPO-ROOT/.github/TaskLogs/Copilot_Scrum.md should exist and its content should not be just a title."
],
designDocReady: [
"$simpleCondition",
"REPO-ROOT/.github/TaskLogs/Copilot_Task.md should exist and its content should not be just a title."
"- REPO-ROOT/.github/TaskLogs/Copilot_Task.md should exist and its content should not be just a title."
],
planDocReady: [
"$simpleCondition",
"REPO-ROOT/.github/TaskLogs/Copilot_Planning.md should exist and its content should not be just a title."
"- REPO-ROOT/.github/TaskLogs/Copilot_Planning.md should exist and its content should not be just a title."
],
execDocReady: [
"$simpleCondition",
"REPO-ROOT/.github/TaskLogs/Copilot_Execution.md should exist and its content should not be just a title."
"- REPO-ROOT/.github/TaskLogs/Copilot_Execution.md should exist and its content should not be just a title."
],
execDocVerified: [
"$simpleCondition",
"REPO-ROOT/.github/TaskLogs/Copilot_Execution.md should exist and it has a `# !!!VERIFIED!!!`."
"- REPO-ROOT/.github/TaskLogs/Copilot_Execution.md should exist and it has a `# !!!VERIFIED!!!`."
],
reviewDocReady: [
"$simpleCondition",
"REPO-ROOT/.github/TaskLogs/Copilot_Review.md should exist and its content should not be just a title."
"- REPO-ROOT/.github/TaskLogs/Copilot_Review.md should exist and its content should not be just a title."
],
reportedDocReady: [
"$simpleCondition",
"$reported-document should exist and its content should not be just a title."
"- $reported-document should exist and its content should not be just a title."
],
clearBuildTestLog: [
"In REPO-ROOT/.github/Scripts, delete both Build.log and Execute.log."
"- In REPO-ROOT/.github/Scripts, delete both Build.log and Execute.log."
],
buildSucceededFragment: [
"REPO-ROOT/.github/Scripts/Build.log must exist and the last several lines shows there is no error"
"- REPO-ROOT/.github/Scripts/Build.log must exist and the last several lines shows there is no error"
],
testPassedFragment: [
"REPO-ROOT/.github/Scripts/Execute.log must exist and the last several lines shows how many test files and test cases passed"
"- REPO-ROOT/.github/Scripts/Execute.log must exist and the last several lines shows how many test files and test cases passed"
]
},
tasks: {
@@ -334,7 +336,7 @@ const entryInput: Entry = {
},
criteria: {
runConditionInSameSession: false,
condition: ["$simpleCondition", "All REPO-ROOT/.github/TaskLogs/Copilot_(Task|Planning|Execution).md must have been deleted."],
condition: ["$simpleCondition", "- All REPO-ROOT/.github/TaskLogs/Copilot_(Task|Planning|Execution).md must have been deleted."],
failureAction: retryFailedCondition()
}
},
@@ -404,7 +406,7 @@ const entryInput: Entry = {
prompt: ["$cppjob", "$review", "# Apply", "$reviewerBoardFiles"],
criteria: {
runConditionInSameSession: false,
condition: ["$simpleCondition", "Every REPO-ROOT/.github/TaskLogs/Copilot_Review*.md must have been deleted."],
condition: ["$simpleCondition", "- Every REPO-ROOT/.github/TaskLogs/Copilot_Review*.md must have been deleted."],
failureAction: retryFailedCondition()
}
},
@@ -419,7 +421,7 @@ const entryInput: Entry = {
prompt: ["$cppjob", "$code", "$user-input"],
criteria: {
runConditionInSameSession: true,
condition: ["$simpleCondition", "Both conditions satisfy: 1) $buildSucceededFragment; 2) $testPassedFragment."],
condition: ["$simpleCondition", "- Both conditions satisfy: 1) $buildSucceededFragment; 2) $testPassedFragment."],
failureAction: retryFailedCondition()
}
},
@@ -428,27 +430,27 @@ const entryInput: Entry = {
requireUserInput: false,
prompt: [
"$defineRepoRoot",
"Call REPO-ROOT/.github/Scripts/copilotGitCommit.ps1",
"DO NOT git push."
"- Call REPO-ROOT/.github/Scripts/copilotGitCommit.ps1",
"- DO NOT git push."
]
},
"git-push": {
model: { category: "driving" },
requireUserInput: false,
prompt: [
"`git add` to add all files.",
"`git status` to list affected files.",
"`git commit -am` everything with this message: [BOT] Backup.",
"`git branch` to see the current branch.",
"`git push` to the current branch.",
"DO NOT run multiple commands at once."
"- `git add` to add all files.",
"- `git status` to list affected files.",
"- `git commit -am` everything with this message: [BOT] Backup.",
"- `git branch` to see the current branch.",
"- `git push` to the current branch.",
"- DO NOT run multiple commands at once."
],
criteria: {
runConditionInSameSession: true,
condition: [
"$simpleCondition",
"`git status` to list file affected, make sure there is nothing uncommited.",
"But it is fine if all uncommited changes are only whitespace related."
"- `git status` to list file affected, make sure there is nothing uncommited.",
"- But it is fine if all uncommited changes are only whitespace related."
],
failureAction: retryFailedCondition()
}

View File

@@ -306,6 +306,66 @@ describe("API: copilot/test/installJobsEntry", () => {
assert.strictEqual(data.result, "OK", `installJobsEntry should succeed: ${JSON.stringify(data)}`);
});
it("rejects entry with invalid model IDs", async () => {
const invalidModelEntryPath = path.join(__dirname, "invalidModelEntry.json");
const fs = await import("node:fs");
// A structurally valid entry but with a model ID that doesn't exist
fs.writeFileSync(invalidModelEntryPath, JSON.stringify({
models: { driving: "nonexistent-model-xyz" },
drivingSessionRetries: [{ modelId: "nonexistent-model-xyz", retries: 1 }],
promptVariables: {},
grid: [],
tasks: {
"dummy-task": { model: { category: "driving" }, prompt: ["hello"], requireUserInput: false }
},
jobs: {}
}));
try {
const data = await fetchJson("/api/copilot/test/installJobsEntry", {
method: "POST",
body: invalidModelEntryPath,
});
assert.strictEqual(data.result, "Rejected", `should reject invalid model: ${JSON.stringify(data)}`);
assert.ok(data.error, "should have error message about invalid model");
assert.ok(data.error.includes("nonexistent-model-xyz"), "error should mention the invalid model name");
} finally {
fs.unlinkSync(invalidModelEntryPath);
}
});
it("rejects entry with invalid drivingSessionRetries model IDs", async () => {
const invalidRetryEntryPath = path.join(__dirname, "invalidRetryEntry.json");
const fs = await import("node:fs");
const modelsData = await fetchJson("/api/copilot/models");
const freeModel = modelsData.models.find((m) => m.multiplier === 0);
// Use a valid model for entry.models but an invalid model in drivingSessionRetries
fs.writeFileSync(invalidRetryEntryPath, JSON.stringify({
models: { driving: freeModel.id },
drivingSessionRetries: [
{ modelId: freeModel.id, retries: 1 },
{ modelId: "nonexistent-retry-model-xyz", retries: 2 }
],
promptVariables: {},
grid: [],
tasks: {
"dummy-task": { model: { category: "driving" }, prompt: ["hello"], requireUserInput: false }
},
jobs: {}
}));
try {
const data = await fetchJson("/api/copilot/test/installJobsEntry", {
method: "POST",
body: invalidRetryEntryPath,
});
assert.strictEqual(data.result, "Rejected", `should reject invalid drivingSessionRetries model: ${JSON.stringify(data)}`);
assert.ok(data.error, "should have error message about invalid model");
assert.ok(data.error.includes("nonexistent-retry-model-xyz"), "error should mention the invalid model name");
assert.ok(data.error.includes("drivingSessionRetries"), "error should mention drivingSessionRetries");
} finally {
fs.unlinkSync(invalidRetryEntryPath);
}
});
it("rejects when session is running", async () => {
const modelsData = await fetchJson("/api/copilot/models");
const freeModel = modelsData.models.find((m) => m.multiplier === 0);

View File

@@ -45,6 +45,8 @@ Prints the following URL for shortcut:
### installJobsEntry
`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`.
- 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.

View File

@@ -234,6 +234,8 @@ When the user holds **Ctrl** and scrolls the mouse wheel over the `#chart-contai
- CSS `transform: scale(...)` with `transform-origin: top left` is applied to the SVG element.
- The default browser scroll/zoom behavior is suppressed (`preventDefault`).
User should be able to zoom in and zoom out also using two touch points (two fingers).
#### Interaction with `ChartNode` which has a `TaskNode` or `CondNode` hint
Clicking it select (exclusive) or unselect the text:
@@ -252,3 +254,16 @@ When a task is being inspected:
- Each task session has its own tab.
- Clicking a tab shows responses from a session using `Session Response Rendering` from `Shared.md`.
- When the selected `ChartNode` is restarted, tabs should be cleared before adding new sessions.
### Phone Specific Layout/Behavior
**IMPORTANT**:
- Phone mode is defined by `max=width: 768px`. The session only applies when the webpage is in phone mode.
- On a PC browser while resizing, it should be able to switch between phone mode and PC mode in any status.
- Build the webpage fully reactive, which means it is not allowed to save the "mode" in anyway even temporarily.
- The `Session Response Part` is not visible at the beginning.
- There is also no draggable bar between it and the `Job Part`.
- When a `ChartNode` is clicked, the `Session Response Part` becomes visible and occupy the whole window.
- At the very right of the tab header of the `Session Response Part`, there should be a "Back" button.
- Clicking the button hide the `Session Response Part` and the `Job Part` becomes available again.

View File

@@ -45,6 +45,8 @@ Prints the following URL for shortcut:
### installJobsEntry
`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`.
- 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.

View File

@@ -234,6 +234,8 @@ When the user holds **Ctrl** and scrolls the mouse wheel over the `#chart-contai
- CSS `transform: scale(...)` with `transform-origin: top left` is applied to the SVG element.
- The default browser scroll/zoom behavior is suppressed (`preventDefault`).
User should be able to zoom in and zoom out also using two touch points (two fingers).
#### Interaction with `ChartNode` which has a `TaskNode` or `CondNode` hint
Clicking it select (exclusive) or unselect the text:
@@ -252,3 +254,16 @@ When a task is being inspected:
- Each task session has its own tab.
- Clicking a tab shows responses from a session using `Session Response Rendering` from `Shared.md`.
- When the selected `ChartNode` is restarted, tabs should be cleared before adding new sessions.
### Phone Specific Layout/Behavior
**IMPORTANT**:
- Phone mode is defined by `max=width: 768px`. The session only applies when the webpage is in phone mode.
- On a PC browser while resizing, it should be able to switch between phone mode and PC mode in any status.
- Build the webpage fully reactive, which means it is not allowed to save the "mode" in anyway even temporarily.
- The `Session Response Part` is not visible at the beginning.
- There is also no draggable bar between it and the `Job Part`.
- When a `ChartNode` is clicked, the `Session Response Part` becomes visible and occupy the whole window.
- At the very right of the tab header of the `Session Response Part`, there should be a "Back" button.
- Clicking the button hide the `Session Response Part` and the `Job Part` becomes available again.

View File

@@ -2,56 +2,56 @@
# yarn lockfile v1
"@github/copilot-darwin-arm64@0.0.403":
version "0.0.403"
resolved "https://registry.yarnpkg.com/@github/copilot-darwin-arm64/-/copilot-darwin-arm64-0.0.403.tgz#e1cbc91f73639c8217fd2cc61498bc311e2af45c"
integrity sha512-dOw8IleA0d1soHnbr/6wc6vZiYWNTKMgfTe/NET1nCfMzyKDt/0F0I7PT5y+DLujJknTla/ZeEmmBUmliTW4Cg==
"@github/copilot-darwin-arm64@0.0.420":
version "0.0.420"
resolved "https://registry.yarnpkg.com/@github/copilot-darwin-arm64/-/copilot-darwin-arm64-0.0.420.tgz#560ca002fa491c04fdb6f74f84fee87e52575c53"
integrity sha512-sj8Oxcf3oKDbeUotm2gtq5YU1lwCt3QIzbMZioFD/PMLOeqSX/wrecI+c0DDYXKofFhALb0+DxxnWgbEs0mnkQ==
"@github/copilot-darwin-x64@0.0.403":
version "0.0.403"
resolved "https://registry.yarnpkg.com/@github/copilot-darwin-x64/-/copilot-darwin-x64-0.0.403.tgz#b246b0b91f31e13650d99c59716fd74ab87465f5"
integrity sha512-aK2jSNWgY8eiZ+TmrvGhssMCPDTKArc0ip6Ul5OaslpytKks8hyXoRbxGD0N9sKioSUSbvKUf+1AqavbDpJO+w==
"@github/copilot-darwin-x64@0.0.420":
version "0.0.420"
resolved "https://registry.yarnpkg.com/@github/copilot-darwin-x64/-/copilot-darwin-x64-0.0.420.tgz#1d5cf40ac4e04bbd69fb0a79abf3743897c5f795"
integrity sha512-2acA93IqXz1uuz3TVUm0Y7BVrBr0MySh1kQa8LqMILhTsG0YHRMm8ybzTp2HA7Mi1tl5CjqMSk163kkS7OzfUA==
"@github/copilot-linux-arm64@0.0.403":
version "0.0.403"
resolved "https://registry.yarnpkg.com/@github/copilot-linux-arm64/-/copilot-linux-arm64-0.0.403.tgz#5453ec3bd565cc92676b450b2edb66e49f60909d"
integrity sha512-KhoR2iR70O6vCkzf0h8/K+p82qAgOvMTgAPm9bVEHvbdGFR7Py9qL5v03bMbPxsA45oNaZAkzDhfTAqWhIAZsQ==
"@github/copilot-linux-arm64@0.0.420":
version "0.0.420"
resolved "https://registry.yarnpkg.com/@github/copilot-linux-arm64/-/copilot-linux-arm64-0.0.420.tgz#e247517854927a14f5c076bfa99309160afec2d7"
integrity sha512-h/IvEryTOYm1HzR2GNq8s2aDtN4lvT4MxldfZuS42CtWJDOfVG2jLLsoHWU1T3QV8j1++PmDgE//HX0JLpLMww==
"@github/copilot-linux-x64@0.0.403":
version "0.0.403"
resolved "https://registry.yarnpkg.com/@github/copilot-linux-x64/-/copilot-linux-x64-0.0.403.tgz#17a7eba380be8553610ee6632d6a81ba229722eb"
integrity sha512-eoswUc9vo4TB+/9PgFJLVtzI4dPjkpJXdCsAioVuoqPdNxHxlIHFe9HaVcqMRZxUNY1YHEBZozy+IpUEGjgdfQ==
"@github/copilot-linux-x64@0.0.420":
version "0.0.420"
resolved "https://registry.yarnpkg.com/@github/copilot-linux-x64/-/copilot-linux-x64-0.0.420.tgz#00d22974499f0fab6354fe4e22f6be59b800ab98"
integrity sha512-iL2NpZvXIDZ+3lw7sO2fo5T0nKmP5dZbU2gdYcv+SFBm/ONhCxIY5VRX4yN/9VkFaa9ePv5JzCnsl3vZINiDxg==
"@github/copilot-sdk@^0.1.4":
version "0.1.23"
resolved "https://registry.yarnpkg.com/@github/copilot-sdk/-/copilot-sdk-0.1.23.tgz#120986bf5719880dedf076c0f2a55f855566ff40"
integrity sha512-0by81bsBQlDKE5VbcegZfUMvPyPm1aXwSGS2rGaMAFxv3ps+dACf1Voruxik7hQTae0ziVFJjuVrlxZoRaXBLw==
"@github/copilot-sdk@^0.1.29":
version "0.1.29"
resolved "https://registry.yarnpkg.com/@github/copilot-sdk/-/copilot-sdk-0.1.29.tgz#8809df61ab53f100f8390234d9946cdc2acae24b"
integrity sha512-GdcN6bJTeesr1HP6IrhN2MznIf1B3ufqd3PX+uKbDLXNriOmP65Ai29/hxzTidNLHyOf6rW4NwmFfkMXiKfCBw==
dependencies:
"@github/copilot" "^0.0.403"
"@github/copilot" "^0.0.420"
vscode-jsonrpc "^8.2.1"
zod "^4.3.6"
"@github/copilot-win32-arm64@0.0.403":
version "0.0.403"
resolved "https://registry.yarnpkg.com/@github/copilot-win32-arm64/-/copilot-win32-arm64-0.0.403.tgz#3cdaee25b2454ceb6d8293f06b258c95307b94ae"
integrity sha512-djWjzCsp2xPNafMyOZ/ivU328/WvWhdroGie/DugiJBTgQL2SP0quWW1fhTlDwE81a3g9CxfJonaRgOpFTJTcg==
"@github/copilot-win32-arm64@0.0.420":
version "0.0.420"
resolved "https://registry.yarnpkg.com/@github/copilot-win32-arm64/-/copilot-win32-arm64-0.0.420.tgz#733c45aced1e42c2877ae44012074abbcce3d55d"
integrity sha512-Njlc2j9vYSBAL+lC6FIEhQ3C+VxO3xavwKnw0ecVRiNLcGLyPrTdzPfPQOmEjC63gpVCqLabikoDGv8fuLPA2w==
"@github/copilot-win32-x64@0.0.403":
version "0.0.403"
resolved "https://registry.yarnpkg.com/@github/copilot-win32-x64/-/copilot-win32-x64-0.0.403.tgz#d22bdd50d9b0674d73981a413df881fd8229f5ce"
integrity sha512-lju8cHy2E6Ux7R7tWyLZeksYC2MVZu9i9ocjiBX/qfG2/pNJs7S5OlkwKJ0BSXSbZEHQYq7iHfEWp201bVfk9A==
"@github/copilot-win32-x64@0.0.420":
version "0.0.420"
resolved "https://registry.yarnpkg.com/@github/copilot-win32-x64/-/copilot-win32-x64-0.0.420.tgz#d45f47f2f08d4bba87760b8afb21af19d1988780"
integrity sha512-rZlH35oNehAP2DvQbu4vQFVNeCh/1p3rUjafBYaEY0Nkhx7RmdrYBileL5U3PtRPPRsBPaq3Qp+pVIrGoCDLzQ==
"@github/copilot@^0.0.403":
version "0.0.403"
resolved "https://registry.yarnpkg.com/@github/copilot/-/copilot-0.0.403.tgz#56e44b5a0640685f0b34507bbb12f47229798940"
integrity sha512-v5jUdtGJReLmE1rmff/LZf+50nzmYQYAaSRNtVNr9g0j0GkCd/noQExe31i1+PudvWU0ZJjltR0B8pUfDRdA9Q==
"@github/copilot@^0.0.420":
version "0.0.420"
resolved "https://registry.yarnpkg.com/@github/copilot/-/copilot-0.0.420.tgz#596349de076566a310836a7e06e6807b87ea6bfe"
integrity sha512-UpPuSjxUxQ+j02WjZEFffWf0scLb23LvuGHzMFtaSsweR+P/BdbtDUI5ZDIA6T0tVyyt6+X1/vgfsJiRqd6jig==
optionalDependencies:
"@github/copilot-darwin-arm64" "0.0.403"
"@github/copilot-darwin-x64" "0.0.403"
"@github/copilot-linux-arm64" "0.0.403"
"@github/copilot-linux-x64" "0.0.403"
"@github/copilot-win32-arm64" "0.0.403"
"@github/copilot-win32-x64" "0.0.403"
"@github/copilot-darwin-arm64" "0.0.420"
"@github/copilot-darwin-x64" "0.0.420"
"@github/copilot-linux-arm64" "0.0.420"
"@github/copilot-linux-x64" "0.0.420"
"@github/copilot-win32-arm64" "0.0.420"
"@github/copilot-win32-x64" "0.0.420"
"@playwright/test@^1.49.0":
version "1.58.2"

View File

@@ -10,7 +10,8 @@
## Goal and Constraints
- Your goal is to finish a design document in `Copilot_Task.md` to address a problem.
- You are only allowed to update `Copilot_Task.md` and mark a task being taken in `Copilot_Scrum.md`.
- You are only allowed to update `Copilot_Task.md`
- When the task comes from `Copilot_Scrum.md`, you should mark a task being taken in `Copilot_Scrum.md`.
- You are not allowed to modify any other files.
- The phrasing of the request may look like asking for code change, but your actual work is to write the design document.
@@ -37,12 +38,14 @@ Ignore this section if there is no "# Problem" in the LATEST chat message
I am starting a fresh new request.
- Find and execute `copilotPrepare.ps1` to clean up everything from the last run.
- This script will clean up everything in `Copilot_Task.md`, `Copilot_Planning.md` and `Copilot_Execution.md`.
- It is normal to find large amount of changes in these 3 files, DO NOT panic.
- After `copilotPrepare.ps1` finishes, copy precisely my problem description in `# Problem` from the LATEST chat message under a `# PROBLEM DESCRIPTION`.
- If the problem description is `Next`:
- Find the first incomplete task in `Copilot_Scrum.md`.
- If the problem description is like `Complete task No.X`:
- Locate the specific task in `Copilot_Scrum.md`.
- Find task from `Copilot_Scrum.md` if the problem description is in the following format:
- `Next`: Find the first incomplete task in `Copilot_Scrum.md`.
- `Complete task No.X`: Locate the specific task in `Copilot_Scrum.md`.
- There is a bullet list of all tasks at the beginning of `# TASKS`. Mark the specific task as being taken by changing `[ ]` to `[x]`.
- Otherwise, the task is the problem description itself.
- Find the details of the specific task, copy everything in this task to `# PROBLEM DESCRIPTION`.
- Add an empty `# UPDATES` section after `# PROBLEM DESCRIPTION`.