Address terminals by workspace ID in the SDK and CLI (#4358)

* feat(terminals): expose workspace ownership through the SDK and CLI

Directory-based terminal creation selected the oldest workspace sharing that path. Expose explicit workspace ownership in the shared public API and CLI, retain ownership and process directories in listings, and acknowledge terminal deletion after teardown.

* fix(file-observer): preserve nested shallow reconciliation scopes

A parent directory scan does not visit its descendants. Collapsing change scopes discarded nested scans and left coalesced native deletions unseen until a full audit.

* test(terminals): extract output waits from end-to-end scenarios
This commit is contained in:
Mohamed Boudra
2026-09-06 10:17:58 +02:00
committed by GitHub
parent 78b285059f
commit c8a417c4fb
30 changed files with 832 additions and 134 deletions
+3 -1
View File
@@ -3408,6 +3408,8 @@ export const ServerInfoStatusPayloadSchema = z
workspaceLabels: z.boolean().optional(),
// COMPAT(workspaceSetupRun): added in v0.7.3, remove gate after 2027-09-02.
workspaceSetupRun: z.boolean().optional(),
// COMPAT(workspaceTerminals): added in v0.7.3, remove gate after 2027-09-05.
workspaceTerminals: z.boolean().optional(),
// COMPAT(checkoutForgeSetAutoMerge): added in v0.2.0-beta.1. Remove the
// feature gate and checkoutGithubSetAutoMerge fallback after 2027-01-17
// once the supported daemon floor is >= v0.2.0.
@@ -6024,7 +6026,7 @@ export const ListTerminalsResponseSchema = z.object({
type: z.literal("list_terminals_response"),
payload: z.object({
cwd: z.string().optional(),
terminals: z.array(TerminalInfoSchema.omit({ cwd: true })),
terminals: z.array(TerminalInfoSchema.partial({ cwd: true })),
requestId: z.string(),
}),
});
@@ -8,6 +8,26 @@ import {
WSHelloMessageSchema,
} from "./messages.js";
test("terminal listings accept older rows and retain new per-terminal directories", () => {
const response = {
type: "list_terminals_response",
payload: {
requestId: "terminal-list",
cwd: "/workspace",
terminals: [{ id: "terminal", name: "Shell", workspaceId: "workspace" }],
},
};
expect(SessionOutboundMessageSchema.parse(response)).toEqual(response);
const withDirectory = {
...response,
payload: {
...response.payload,
terminals: [{ ...response.payload.terminals[0], cwd: "/workspace/subdirectory" }],
},
};
expect(SessionOutboundMessageSchema.parse(withDirectory)).toEqual(withDirectory);
});
const LegacySubAgentToolCallSchema = z.object({
type: z.literal("tool_call"),
callId: z.string(),