Skip to content

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.

LocationContentsProtobuf package
proto/dline/*.protoApplication and Webview services such as task.proto, state.proto, mcp.protodline
proto/dline/host/IDE host services: diff, env, language, testing, window, workspacedline.host
proto/dline/models/Model metadata such as ApiFormatdline.models
proto/dline/provider/Provider-specific messagesdline.provider
proto/dline/common.protoShared messagesdline

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).

Terminal window
npm run protos

scripts/build-proto.mjs collects proto/**/*.proto, cleans the previous output, and regenerates:

OutputContents
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.tsClients used by the Webview
dist-standalone/proto/descriptor_set.pbDescriptor 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.

  1. Define the contract: add the message and RPC to the .proto file that owns the domain; host capabilities go under proto/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, use reserved for its number and name where compatibility requires it.

  2. Regenerate: run npm run protos and check that the generated diff has no unexpected deletions or namespace changes.

  3. Implement the handler: application service handlers live at src/core/controller/<directory>/<method>.ts, where the directory is the service name without the Service suffix and with a lowercase first letter. For example, TaskService.cancelTask in task.proto is implemented by src/core/controller/task/cancelTask.ts. The generator imports and registers handlers by this convention in src/generated/hosts/vscode/protobus-services.ts, so there is no second registry to maintain. Host bridge handlers live under src/hosts/vscode/hostbridge/<service>/.

  4. Call it: the Webview imports generated clients from webview-ui/src/services/grpc-client.ts and message types from @shared/proto/dline/.... When an RPC owns an interaction, do not send an ad-hoc postMessage object instead.

  5. Verify: run npm run check-types and npm run lint, then the relevant handler, conversion, and transport tests. For streaming RPCs, also test cancellation and disposal.

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.