Protobuf and ProtoBus
Dline uses Protobuf for typed contracts between the Webview, the core Controller, and the host bridge. RPC between the Webview and the extension uses ProtoBus request/response envelopes carried over VS Code postMessage; the generated API looks like gRPC, but no network socket is involved.
Source layout
Section titled “Source layout”| Location | Contents | Protobuf package |
|---|---|---|
proto/dline/*.proto | Application and Webview services such as task.proto, state.proto, mcp.proto | dline |
proto/dline/host/ | IDE host services: diff, env, language, testing, window, workspace | dline.host |
proto/dline/models/ | Model metadata such as ApiFormat | dline.models |
proto/dline/provider/ | Provider-specific messages | dline.provider |
proto/dline/common.proto | Shared messages | dline |
Import paths are rooted at dline/. Naming conventions: services use PascalCaseService, RPC methods use camelCase, messages and enums use PascalCase, and fields use snake_case (converted when TypeScript is generated).
Generate code
Section titled “Generate code”npm run protosscripts/build-proto.mjs collects proto/**/*.proto, cleans the previous output, and regenerates:
| Output | Contents |
|---|---|
src/shared/proto/ | TypeScript message types and barrel files |
src/generated/grpc-js/ | grpc-js service definitions |
src/generated/nice-grpc/ | nice-grpc clients |
src/generated/hosts/ | ProtoBus service registration and host bridge glue |
webview-ui/src/services/grpc-client.ts | Clients used by the Webview |
dist-standalone/proto/descriptor_set.pb | Descriptor set |
After generation, the postprotos script formats the generated code with Biome. Never edit generated files by hand; if the output is wrong, change the .proto source or the generator.
Add an RPC
Section titled “Add an RPC”-
Define the contract: add the message and RPC to the
.protofile that owns the domain; host capabilities go underproto/dline/host/. Spell out optional, repeated, and default semantics, and how errors and cancellation behave. Never reuse an existing field number for a different meaning; when removing a field, usereservedfor its number and name where compatibility requires it. -
Regenerate: run
npm run protosand check that the generated diff has no unexpected deletions or namespace changes. -
Implement the handler: application service handlers live at
src/core/controller/<directory>/<method>.ts, where the directory is the service name without theServicesuffix and with a lowercase first letter. For example,TaskService.cancelTaskintask.protois implemented bysrc/core/controller/task/cancelTask.ts. The generator imports and registers handlers by this convention insrc/generated/hosts/vscode/protobus-services.ts, so there is no second registry to maintain. Host bridge handlers live undersrc/hosts/vscode/hostbridge/<service>/. -
Call it: the Webview imports generated clients from
webview-ui/src/services/grpc-client.tsand message types from@shared/proto/dline/.... When an RPC owns an interaction, do not send an ad-hocpostMessageobject instead. -
Verify: run
npm run check-typesandnpm run lint, then the relevant handler, conversion, and transport tests. For streaming RPCs, also test cancellation and disposal.
Enum and message conversion
Section titled “Enum and message conversion”Some enums used for persistence or UI need explicit two-way conversions in src/shared/proto-conversions/. When adding an enum value, check the conversion functions in both directions.
Adding a field to a Proto message does not automatically carry it into Controller state projection, Webview defaults, persistence, or validation. Connect each step of the data flow explicitly.