Skip to content

Commit 0b9a595

Browse files
Merge branch 'main' into issue/413
2 parents 6fd04d6 + 147171e commit 0b9a595

170 files changed

Lines changed: 12083 additions & 1264 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/e2e.yml

Lines changed: 31 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,6 @@ on:
44
workflow_dispatch:
55
pull_request:
66
types: [opened, reopened, ready_for_review, synchronize]
7-
paths:
8-
- "src/**"
9-
- "webview-ui/**"
10-
- "apps/vscode-e2e/**"
11-
- "packages/core/**"
12-
- "package.json"
13-
- "pnpm-lock.yaml"
14-
- "turbo.json"
15-
- ".github/actions/setup-node-pnpm/**"
167
merge_group:
178
types: [checks_requested]
189

@@ -23,9 +14,40 @@ jobs:
2314
steps:
2415
- name: Checkout code
2516
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
17+
18+
- name: Restore mocked E2E pass marker
19+
id: e2e-marker
20+
uses: actions/cache/restore@0057852bfaa89a56745cba8c7296529d2fc39830 # v4
21+
with:
22+
path: .cache/e2e-pass
23+
# packages/** is intentionally broader than the old packages/core/** to avoid false cache hits when any package changes
24+
# base_ref is included to prevent same-repo branches from stuffing a pass marker for a different base
25+
key: ${{ runner.os }}-mocked-e2e-${{ github.base_ref }}-${{ hashFiles('src/**', 'webview-ui/**', 'apps/vscode-e2e/**', 'packages/**', 'package.json', 'pnpm-lock.yaml', 'pnpm-workspace.yaml', 'turbo.json', '.github/workflows/e2e.yml', '.github/actions/setup-node-pnpm/**') }}
26+
27+
- name: Use cached mocked E2E result
28+
if: github.event_name == 'pull_request' && steps.e2e-marker.outputs.cache-hit == 'true'
29+
run: echo "Skipping mocked E2E tests because this source hash already passed."
30+
2631
- name: Setup Node.js and pnpm
32+
if: github.event_name != 'pull_request' || steps.e2e-marker.outputs.cache-hit != 'true'
2733
uses: ./.github/actions/setup-node-pnpm
2834
- name: Install xvfb
35+
if: github.event_name != 'pull_request' || steps.e2e-marker.outputs.cache-hit != 'true'
2936
run: sudo apt-get install -y xvfb
3037
- name: Run mocked E2E tests
38+
id: run-e2e
39+
# merge_group and workflow_dispatch always run; cache skip is pull_request only
40+
if: github.event_name != 'pull_request' || steps.e2e-marker.outputs.cache-hit != 'true'
3141
run: xvfb-run -a pnpm --filter @roo-code/vscode-e2e test:ci:mock
42+
43+
- name: Write mocked E2E pass marker
44+
if: steps.e2e-marker.outputs.cache-hit != 'true' && steps.run-e2e.outcome == 'success'
45+
run: mkdir -p .cache/e2e-pass && date -u > .cache/e2e-pass/passed
46+
47+
- name: Save mocked E2E pass marker
48+
if: steps.e2e-marker.outputs.cache-hit != 'true' && steps.run-e2e.outcome == 'success'
49+
continue-on-error: true
50+
uses: actions/cache/save@0057852bfaa89a56745cba8c7296529d2fc39830 # v4
51+
with:
52+
path: .cache/e2e-pass
53+
key: ${{ steps.e2e-marker.outputs.cache-primary-key }}

.github/workflows/marketplace-publish.yml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ on:
99
jobs:
1010
publish-stable:
1111
runs-on: ubuntu-latest
12+
environment: marketplace-production
1213
permissions:
1314
contents: write
1415

@@ -29,6 +30,13 @@ jobs:
2930
test "$package_name" = "zoo-code"
3031
test "$publisher" = "ZooCodeOrganization"
3132
33+
- name: Validate publish ref
34+
run: |
35+
if [ "$GITHUB_EVENT_NAME" = "workflow_dispatch" ] && [ "$GITHUB_REF_NAME" != "main" ]; then
36+
echo "Manual stable publishes must run from main, not ${GITHUB_REF_NAME}."
37+
exit 1
38+
fi
39+
3240
- name: Validate release tag
3341
if: github.event_name == 'push'
3442
run: |

.github/workflows/nightly-publish.yml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ concurrency:
1515
jobs:
1616
publish-prerelease:
1717
runs-on: ubuntu-latest
18+
environment: marketplace-prerelease
1819

1920
steps:
2021
- name: Checkout code
@@ -33,6 +34,13 @@ jobs:
3334
test "$package_name" = "zoo-code"
3435
test "$publisher" = "ZooCodeOrganization"
3536
37+
- name: Validate publish ref
38+
run: |
39+
if [ "$GITHUB_REF_NAME" != "main" ]; then
40+
echo "Pre-release publishes must run from main, not ${GITHUB_REF_NAME}."
41+
exit 1
42+
fi
43+
3644
- name: Set pre-release version
3745
id: version
3846
env:

PRIVACY.md

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -40,13 +40,6 @@ go—and, importantly, where they don't.
4040
We retain telemetry only as long as needed for product analytics and debugging.
4141
Telemetry does **not** collect your code or AI prompts, and you can opt out at
4242
any time through the settings.
43-
- **Zoo Code Observability (Authenticated Subscribers Only):** If you sign in to
44-
Zoo Code and have an active subscription, Zoo Code will send LLM usage
45-
telemetry to the Zoo Code backend (zoocode.dev). This includes task ID, AI
46-
provider name, model name, token counts (input/output/cache), and estimated
47-
cost. This data is linked to your authenticated Zoo Code account. You can stop
48-
this collection at any time by signing out via the Zoo Code badge in the chat
49-
area.
5043
- **Marketplace Requests**: When you browse or search the Marketplace for Model
5144
Configuration Profiles (MCPs) or Custom Modes, Zoo Code makes a secure API
5245
call to Zoo Code's backend servers to retrieve listing information. These
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
{
2+
"fixtures": [
3+
{
4+
"match": {
5+
"userMessage": "TERMINAL_PROFILE_E2E_OVERRIDE"
6+
},
7+
"response": {
8+
"toolCalls": [
9+
{
10+
"name": "execute_command",
11+
"arguments": "{\"command\":\"printf 'zoo-profile-override-ok\\\\n' > terminal-profile-e2e/terminal-profile-override.txt\"}",
12+
"id": "call_terminal_profile_override_001"
13+
}
14+
]
15+
}
16+
},
17+
{
18+
"match": {
19+
"userMessage": "TERMINAL_PROFILE_E2E_DEFAULT"
20+
},
21+
"response": {
22+
"toolCalls": [
23+
{
24+
"name": "execute_command",
25+
"arguments": "{\"command\":\"printf 'zoo-profile-default-ok\\\\n' > terminal-profile-e2e/terminal-profile-default.txt\"}",
26+
"id": "call_terminal_profile_default_001"
27+
}
28+
]
29+
}
30+
}
31+
]
32+
}
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
import { LLMock } from "@copilotkit/aimock"
2+
import type { ChatCompletionRequest } from "@copilotkit/aimock"
3+
4+
import { toolResultContains } from "./tool-result"
5+
6+
const SUBTASK_PARENT_MARKER = "SUBTASK_PARENT_CANCELLATION_SMOKE"
7+
const SUBTASK_CHILD_MARKER = "SUBTASK_CHILD_CALCULATOR_SMOKE"
8+
9+
const SUBTASK_CHILD_PROMPT = `${SUBTASK_CHILD_MARKER}: Ask the user exactly this follow-up question: What is the square root of 81? After the user answers, complete with only the answer.`
10+
export const SUBTASK_PARENT_PROMPT = `${SUBTASK_PARENT_MARKER}: Use the new_task tool exactly once. Create an ask-mode subtask with this exact message: "${SUBTASK_CHILD_PROMPT}" Do not answer directly.`
11+
export const SUBTASK_CHILD_FOLLOWUP_ANSWER = "9"
12+
13+
const requestContains = (req: ChatCompletionRequest, expected: string[]) => {
14+
const rawRequest = JSON.stringify(req)
15+
return expected.every((text) => rawRequest.includes(text))
16+
}
17+
18+
const completionAfterAnswer = (followupId: string, completionId: string) => ({
19+
match: {
20+
predicate: (req: ChatCompletionRequest) =>
21+
// Preferred: structured tool-result message carries the followup answer.
22+
toolResultContains(req, followupId, [SUBTASK_CHILD_FOLLOWUP_ANSWER]) ||
23+
// Fallback 1: answer present alongside the tool-call ID but not in a role:tool message.
24+
requestContains(req, [followupId, SUBTASK_CHILD_FOLLOWUP_ANSWER]) ||
25+
// Fallback 2: answer arrives as a bare user message after task resume (no tool-call ID context).
26+
requestContains(req, [
27+
SUBTASK_CHILD_MARKER,
28+
`<user_message>\\n${SUBTASK_CHILD_FOLLOWUP_ANSWER}\\n</user_message>`,
29+
]),
30+
},
31+
response: {
32+
toolCalls: [
33+
{
34+
name: "attempt_completion",
35+
arguments: JSON.stringify({ result: "9" }),
36+
id: completionId,
37+
},
38+
],
39+
},
40+
})
41+
42+
export function addSubtaskFixtures(mock: InstanceType<typeof LLMock>) {
43+
mock.addFixture({
44+
match: {
45+
userMessage: new RegExp(SUBTASK_PARENT_MARKER),
46+
},
47+
response: {
48+
toolCalls: [
49+
{
50+
name: "new_task",
51+
arguments: JSON.stringify({
52+
mode: "ask",
53+
message: SUBTASK_CHILD_PROMPT,
54+
}),
55+
id: "call_subtasks_parent_new_task_001",
56+
},
57+
],
58+
},
59+
})
60+
61+
mock.addFixture({
62+
match: {
63+
userMessage: new RegExp(SUBTASK_CHILD_MARKER),
64+
},
65+
response: {
66+
toolCalls: [
67+
{
68+
name: "ask_followup_question",
69+
arguments: JSON.stringify({
70+
question: "What is the square root of 81?",
71+
follow_up: [{ text: SUBTASK_CHILD_FOLLOWUP_ANSWER }],
72+
}),
73+
id: "call_subtasks_child_followup_001",
74+
},
75+
],
76+
},
77+
})
78+
79+
mock.addFixture(completionAfterAnswer("call_subtasks_child_followup_001", "call_subtasks_child_completion_002"))
80+
81+
mock.addFixture({
82+
match: {
83+
toolCallId: "call_subtasks_parent_new_task_001",
84+
},
85+
response: {
86+
toolCalls: [
87+
{
88+
name: "attempt_completion",
89+
arguments: JSON.stringify({ result: "Parent task resumed" }),
90+
id: "call_subtasks_parent_completion_003",
91+
},
92+
],
93+
},
94+
})
95+
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
import { LLMock } from "@copilotkit/aimock"
2+
3+
import { toolResultContains } from "./tool-result"
4+
5+
type TerminalProfileToolCall = {
6+
name: "execute_command" | "attempt_completion"
7+
params: Record<string, unknown>
8+
id: string
9+
}
10+
11+
type TerminalProfileFixture = {
12+
toolCallId: string
13+
expected: string[]
14+
toolCalls: TerminalProfileToolCall[]
15+
}
16+
17+
export function addTerminalProfileResultFixtures(mock: InstanceType<typeof LLMock>) {
18+
const fixtures: TerminalProfileFixture[] = [
19+
{
20+
toolCallId: "call_terminal_profile_override_001",
21+
expected: ["Exit code: 0"],
22+
toolCalls: [
23+
{
24+
name: "attempt_completion",
25+
params: { result: "Ran the command using the Zoo E2E Bash profile override." },
26+
id: "call_terminal_profile_override_002",
27+
},
28+
],
29+
},
30+
{
31+
toolCallId: "call_terminal_profile_default_001",
32+
expected: ["Exit code: 0"],
33+
toolCalls: [
34+
{
35+
name: "attempt_completion",
36+
params: { result: "Ran the command using the default terminal profile." },
37+
id: "call_terminal_profile_default_002",
38+
},
39+
],
40+
},
41+
]
42+
43+
for (const fixture of fixtures) {
44+
mock.addFixture({
45+
match: {
46+
toolCallId: fixture.toolCallId,
47+
predicate: (req) => toolResultContains(req, fixture.toolCallId, fixture.expected),
48+
},
49+
response: {
50+
toolCalls: fixture.toolCalls.map((toolCall) => ({
51+
name: toolCall.name,
52+
arguments: JSON.stringify(toolCall.params),
53+
id: toolCall.id,
54+
})),
55+
},
56+
})
57+
}
58+
}

apps/vscode-e2e/src/runTest.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,11 @@ import { LLMock } from "@copilotkit/aimock"
77

88
import { addApplyDiffResultFixtures } from "./fixtures/apply-diff"
99
import { addExecuteCommandResultFixtures } from "./fixtures/execute-command"
10+
import { addTerminalProfileResultFixtures } from "./fixtures/terminal-profile"
1011
import { addListFilesResultFixtures } from "./fixtures/list-files"
1112
import { addReadFileResultFixtures } from "./fixtures/read-file"
1213
import { addSearchFilesResultFixtures } from "./fixtures/search-files"
14+
import { addSubtaskFixtures } from "./fixtures/subtasks"
1315
import { addUseMcpToolResultFixtures } from "./fixtures/use-mcp-tool"
1416
import { addWriteToFileResultFixtures } from "./fixtures/write-to-file"
1517

@@ -107,9 +109,11 @@ async function main() {
107109
if (!isRecord) {
108110
addApplyDiffResultFixtures(mock)
109111
addExecuteCommandResultFixtures(mock)
112+
addTerminalProfileResultFixtures(mock)
110113
addListFilesResultFixtures(mock)
111114
addReadFileResultFixtures(mock)
112115
addSearchFilesResultFixtures(mock)
116+
addSubtaskFixtures(mock)
113117
addUseMcpToolResultFixtures(mock)
114118
addWriteToFileResultFixtures(mock)
115119

0 commit comments

Comments
 (0)