源码快照 @ 2026-09-01 (source-only, 剔除非源码素材)
Publish Claude Code Binaries / Stage and publish binaries (push) Canceled after 0s
Publish Tutti App Runtime / Build darwin-arm64 (push) Canceled after 0s
Publish Tutti App Runtime / Build darwin-amd64 (push) Canceled after 0s
Publish Tutti App Runtime / Build linux-amd64 (push) Canceled after 0s
Publish Tutti App Runtime / Build windows-amd64 (push) Canceled after 0s
Sync Tutti Agent Skills / sync (push) Canceled after 0s
Windows Agent Adapters / Test Windows agent adapters (push) Canceled after 0s
Windows Daemon Adapters / Test Windows daemon adapters (push) Canceled after 0s
Publish Tutti App Runtime / Publish runtime catalog (push) Canceled after 0s
Publish Claude Code Binaries / Stage and publish binaries (push) Canceled after 0s
Publish Tutti App Runtime / Build darwin-arm64 (push) Canceled after 0s
Publish Tutti App Runtime / Build darwin-amd64 (push) Canceled after 0s
Publish Tutti App Runtime / Build linux-amd64 (push) Canceled after 0s
Publish Tutti App Runtime / Build windows-amd64 (push) Canceled after 0s
Sync Tutti Agent Skills / sync (push) Canceled after 0s
Windows Agent Adapters / Test Windows agent adapters (push) Canceled after 0s
Windows Daemon Adapters / Test Windows daemon adapters (push) Canceled after 0s
Publish Tutti App Runtime / Publish runtime catalog (push) Canceled after 0s
This commit is contained in:
@@ -0,0 +1,345 @@
|
||||
# API Contracts
|
||||
|
||||
Use OpenAPI as the single source of truth for daemon HTTP contracts.
|
||||
|
||||
## Source Of Truth
|
||||
|
||||
The canonical daemon API contract lives at:
|
||||
|
||||
```text
|
||||
services/tuttid/api/openapi/tuttid.v1.yaml
|
||||
```
|
||||
|
||||
Generated outputs derive from that spec for:
|
||||
|
||||
- Go transport types and server contract code under `services/tuttid/api/generated`
|
||||
- TypeScript client output under `packages/clients/tuttid-ts/src/generated`
|
||||
|
||||
Shared workbench snapshot schemas are the one exception inside the OpenAPI
|
||||
components section. Their canonical source lives at:
|
||||
|
||||
```text
|
||||
packages/workbench/snapshot/src/schema.json
|
||||
```
|
||||
|
||||
The OpenAPI `WorkbenchSnapshot*` component schemas are synchronized from that
|
||||
shared package so the open-source desktop and TSH can reuse the same workbench
|
||||
snapshot contract while `tuttid` still exposes a normal OpenAPI transport
|
||||
contract. The daemon workspace service also consumes a generated Go contract
|
||||
file derived from the same shared snapshot sources for schema-version, enum,
|
||||
and limit validation.
|
||||
|
||||
`WorkbenchSnapshot*` is an explicit shared-contract exception to the usual
|
||||
"generated code stays in transport" default. When it avoids duplicate
|
||||
hand-maintained mirrors, the synchronized Go snapshot contract may be reused
|
||||
inside `packages/workbench/service`, with `services/tuttid/service/workspace`
|
||||
consuming that shared package as a host adapter. This exception applies to the
|
||||
shared workbench snapshot contract only, not to arbitrary generated request or
|
||||
response DTOs.
|
||||
|
||||
The shared business event stream contract is the other explicit repository
|
||||
exception to the HTTP OpenAPI source-of-truth rule. Its canonical sources live
|
||||
at:
|
||||
|
||||
```text
|
||||
packages/events/protocol/schemas/**/*.json
|
||||
packages/events/protocol/definitions/**/*.event.json
|
||||
```
|
||||
|
||||
Those file-based sources generate:
|
||||
|
||||
- TypeScript protocol contracts, validators, and topic registry metadata under
|
||||
`packages/events/protocol/src/generated`
|
||||
- Go transport contracts and topic registry metadata under
|
||||
`services/tuttid/api/events/generated`
|
||||
|
||||
This exception is limited to the shared business event protocol boundary. The
|
||||
shared package owns the schema-first event sources and the generated
|
||||
TypeScript-facing protocol surface. The daemon still owns its WebSocket route,
|
||||
its authoritative event catalog composition, and the generated Go transport
|
||||
outputs that live under `services/tuttid/api/events/generated`.
|
||||
|
||||
This exception does not create a second source of truth for daemon HTTP routes
|
||||
or a license to hand-maintain competing event DTOs in desktop or daemon code.
|
||||
|
||||
## Repository Rules
|
||||
|
||||
- change the OpenAPI spec before changing daemon HTTP request or response shapes
|
||||
- change `packages/workbench/snapshot/src/schema.json` before changing
|
||||
`WorkbenchSnapshot*` component schemas in the OpenAPI spec
|
||||
- do not hand-maintain parallel transport DTOs once the generated boundary exists
|
||||
- do not hand-edit OpenAPI `WorkbenchSnapshot*` schemas; run the sync command
|
||||
instead
|
||||
- do not hand-edit the daemon workbench snapshot contract constants; run the Go
|
||||
sync command instead
|
||||
- do not hand-edit generated business event protocol TypeScript or Go outputs;
|
||||
run the event protocol generator instead
|
||||
- do not assume generated Go structs enforce every OpenAPI or JSON Schema
|
||||
constraint at runtime
|
||||
- keep generated code limited to the transport layer
|
||||
- exception: the synchronized Go `WorkbenchSnapshot*` contract may be reused in
|
||||
`packages/workbench/service`, with host adapters such as
|
||||
`services/tuttid/service/workspace` consuming that shared package, because it
|
||||
is a repository-owned shared contract rather than an arbitrary route-local DTO
|
||||
- exception: the shared business event protocol sources live in
|
||||
`packages/events/protocol`, while daemon-owned generated Go transport outputs
|
||||
live in `services/tuttid/api/events/generated`; keep daemon-side event-route
|
||||
orchestration, catalog composition, and business workflows hand-written
|
||||
- keep business rules, orchestration, and persistence hand-written
|
||||
- keep compatibility aliases such as `/healthz` out of the versioned generated client surface unless they are part of the intended public contract
|
||||
|
||||
## Agent Provider And Target Identity
|
||||
|
||||
Agent `provider` values are an open, validated execution identity. OpenAPI,
|
||||
generated clients, AgentGUI, and Workbench must preserve an unknown valid
|
||||
provider string instead of coercing it to a built-in provider. Closed enums are
|
||||
appropriate only for preferences that intentionally select among built-in
|
||||
defaults.
|
||||
|
||||
`AgentTarget.launchRef` is a discriminated union. `builtin_local` is the
|
||||
canonical built-in discriminator and `agent_extension` identifies a fixed,
|
||||
verified extension installation. The daemon may accept legacy `local_cli` data
|
||||
while reading durable state, but API output and new writes must never emit it.
|
||||
Add a new union member before adding a new launch mechanism; do not overload a
|
||||
provider string with launch or installation state.
|
||||
|
||||
## Agent Provider CLI Update Status
|
||||
|
||||
Provider status is local by default. `includeUpdates` is the explicit opt-in for
|
||||
cached remote update discovery. `refresh` bypasses only local readiness;
|
||||
`refreshUpdates` bypasses only update metadata and has no effect unless
|
||||
`includeUpdates` is also true. Keep `includeNetwork` independent from both.
|
||||
|
||||
Every provider status carries a provider-neutral `update` object. Nullable
|
||||
`updateAvailable` distinguishes unchecked or non-comparable state from a known
|
||||
up-to-date result. Remote discovery errors belong in update `reasonCode` and
|
||||
must not turn a valid local readiness snapshot into an HTTP failure.
|
||||
|
||||
Provider auth status distinguishes credential presence from remote proof:
|
||||
`configured` means local credentials exist, `authenticated` means a
|
||||
provider-backed operation succeeded, `required` means authentication is known
|
||||
to be missing or rejected, and `unknown` means no settled evidence exists.
|
||||
Clients may use `availability.status` to decide launchability; they must not
|
||||
coerce `configured` to `authenticated` for display or analytics.
|
||||
|
||||
`update` is an explicit action id, not an alias for `install`. The daemon may
|
||||
offer it only when both the provider descriptor and the resolved installation
|
||||
prove a supported managed source. See [Agent Provider CLI Updates](../architecture/agent-provider-cli-updates.md).
|
||||
|
||||
## Error Contract
|
||||
|
||||
Daemon API failures should use the shared protocol-error shape in
|
||||
`ApiErrorDetails` instead of relying on raw natural-language messages.
|
||||
|
||||
Rules:
|
||||
|
||||
- classify daemon-facing API failures through `services/tuttid/apierrors`
|
||||
- treat `code` as the stable top-level contract for programmatic handling
|
||||
- use `reason` to narrow copy lookup within a stable `code`
|
||||
- use `params` only for structured interpolation or machine-readable context
|
||||
- keep `developerMessage` diagnostic; it is not a user-facing copy contract
|
||||
- use `correlationId` for cross-layer diagnostics when a request needs traceable failure context
|
||||
- do not make renderer UX depend on exact `developerMessage` strings
|
||||
- keep status-code mapping and transport validation at the `api` seam even when `apierrors` classifies the failure
|
||||
|
||||
Current canonical `ApiErrorDetails` fields are:
|
||||
|
||||
- `code`
|
||||
- `reason`
|
||||
- `params`
|
||||
- `retryable`
|
||||
- `developerMessage`
|
||||
- `correlationId`
|
||||
|
||||
TypeScript consumers should normalize transport failures through
|
||||
`@tutti-os/client-tuttid-ts` instead of re-inspecting raw response payloads in
|
||||
each feature. Renderer and preload code may use `code`, `reason`, `params`, and
|
||||
`retryable` for behavior and i18n lookup, while `developerMessage` and
|
||||
`correlationId` remain diagnostic support fields.
|
||||
|
||||
## Resolved Defaults
|
||||
|
||||
When a route returns resolved defaults or effective settings, the response must
|
||||
carry daemon-owned truth rather than echoing request input back to the client.
|
||||
|
||||
Rules:
|
||||
|
||||
- use explicit fields such as `effectiveSettings` for daemon-resolved values
|
||||
- do not mirror request `settings` back in a response just to help clients keep
|
||||
local fallback state alive
|
||||
- return `null` or omit a field when the daemon cannot resolve it
|
||||
- do not synthesize fallback defaults in renderer code when the daemon leaves a
|
||||
field unresolved
|
||||
|
||||
`composer-options` follows this rule: request `settings` are input overrides,
|
||||
while response `effectiveSettings` is the only contract for resolved homepage
|
||||
composer defaults.
|
||||
|
||||
The same endpoint accepts an optional `section`: `core` returns the model,
|
||||
reasoning, speed, permission, and effective-settings projection;
|
||||
`capabilities` returns skills, commands, and capability-catalog data; omitted
|
||||
or `full` preserves the combined response for compatibility. Sectioned
|
||||
responses are additive projections, not alternate sources of resolved settings.
|
||||
|
||||
## Desktop Agent Conversation Detail Mode
|
||||
|
||||
`agentConversationDetailMode` is a global desktop preference, not a provider-specific
|
||||
composer default. Keep it on the top-level desktop preferences contract and the
|
||||
matching desktop preferences event payload; do not place it under
|
||||
`agentComposerDefaultsByProvider`.
|
||||
|
||||
The stored value is the enum `coding | general`. Daemon and desktop shared
|
||||
normalizers must treat missing, empty, or unknown values as `coding` so migrated
|
||||
profiles keep the engineering-oriented default. `coding` does not inject extra
|
||||
prompt guidance; it leaves provider defaults intact. `general` injects the
|
||||
Codex-style `Non-technical UI` developer instruction section for new agent
|
||||
sessions. For Codex app-server sessions, inject this through the session-scoped
|
||||
Codex config before thread creation, not as a repeated per-turn
|
||||
`turn/start.collaborationMode.settings.developer_instructions` override. The
|
||||
same Codex session config should also include a Tutti-owned diagnostic marker
|
||||
under `[tutti] conversationDetailMode = "coding" | "general"` so runtime
|
||||
inspection can distinguish the global Tutti setting from Codex's own desktop
|
||||
preferences. Do not confuse that with Codex collaboration mode presets: when
|
||||
`collaborationMode/list` returns Default or Plan `developer_instructions`, the
|
||||
Codex app-server adapter must pass the active preset instructions in
|
||||
`turn/start.collaborationMode.settings.developer_instructions` so the active
|
||||
mode matches Codex App behavior. Plan Mode and explicit planning-only flows
|
||||
remain higher priority than conversation detail mode prompt guidance.
|
||||
|
||||
## Deleted Agent Conversation Retention
|
||||
|
||||
`deletedAgentConversationRetentionDays` is a device-global desktop preference
|
||||
and must stay aligned across the OpenAPI `DesktopPreferences` schema and the
|
||||
desktop preferences event schema. It is the closed integer set `15 | 30`; the
|
||||
daemon normalizes missing durable values to 30 and rejects other transport
|
||||
values. The renderer must publish the complete preference object through the
|
||||
existing authoritative preference event flow rather than storing this setting
|
||||
locally.
|
||||
|
||||
The Workspace deleted-conversation collection is
|
||||
`/v1/workspaces/{workspaceID}/deleted-agent-sessions`:
|
||||
|
||||
- `GET` lists topmost deleted Session components: either a canonical root or a
|
||||
child whose parent is not deleted. `searchQuery` searches titles only;
|
||||
`projectPath` selects one exact original project, while
|
||||
`projectScope=unscoped` selects Sessions without a project. Omitting both is
|
||||
the all-project default. `cursor` and `limit` provide stable
|
||||
`updatedAtUnixMs DESC, agentSessionId ASC` paging.
|
||||
- The list response includes filtered `totalCount`, unfiltered
|
||||
`workspaceTotalCount`, and all original non-empty project paths as
|
||||
`projectOptions`, including paths whose project registration is unavailable.
|
||||
A row explicitly reports whether it is restorable and why a legacy tombstone
|
||||
is unavailable.
|
||||
- `POST .../{agentSessionID}/restore` restores the exact complete component without
|
||||
starting provider work. `DELETE .../{agentSessionID}` permanently removes one
|
||||
topmost deleted component, and collection `DELETE` permanently removes every
|
||||
topmost deleted component in that Workspace regardless of visible filters.
|
||||
|
||||
Permanent-delete endpoints return only aggregate Session, Message, and payload
|
||||
byte counts. They use the daemon idle gate; restore does not. The API reports an
|
||||
idle conflict as service unavailable and a maintenance failure as a Workspace
|
||||
operation failure. Ordinary and typed high-risk confirmations are desktop
|
||||
interactions, not HTTP request fields. Canonical deletion, Tutti Mode cleanup,
|
||||
and the durable Session-scoped runtime/copied-attachment cleanup item commit
|
||||
together. Because the current runtime and copied-attachment roots are keyed
|
||||
only by Session ID, a pending item fences reuse of that ID across all
|
||||
Workspaces; filesystem cleanup failure does not change the successful HTTP
|
||||
result and is retried in a later idle window.
|
||||
|
||||
`POST /v1/agent-maintenance/deleted-conversations/purge` remains the
|
||||
daemon-wide maintenance sweep without a Workspace path. Automatic retention is
|
||||
daemon-owned and is not exposed as a second client scheduler. After an explicit
|
||||
daemon-wide sweep, the daemon may make a strictly bounded best-effort
|
||||
compaction attempt for a small database with substantial free pages; this
|
||||
optional post-step is not run by automatic maintenance and is not part of the
|
||||
aggregate response contract.
|
||||
|
||||
## Desktop Lab Preferences
|
||||
|
||||
`lab` is a top-level desktop preference group and must stay on both the
|
||||
`DesktopPreferences` OpenAPI schema and the matching desktop preferences event
|
||||
payload. It is not renderer-local state and it is not part of provider-specific
|
||||
composer defaults.
|
||||
|
||||
The group owns:
|
||||
|
||||
- `enabled`: whether the hidden Lab settings tab is exposed after the developer
|
||||
panel is visible.
|
||||
- `features`: per-experiment toggles such as AgentGUI composer worktree launch
|
||||
and workbench lab shortcuts.
|
||||
- `shortcuts`: user-configured workbench shortcut bindings. Missing, empty, or
|
||||
over-length bindings normalize to unbound/null before publishing.
|
||||
|
||||
Renderer settings may optimistically update Lab preferences, but the daemon
|
||||
desktop preferences store and `preferences.desktop.updated` event remain the
|
||||
authoritative durability and broadcast path.
|
||||
|
||||
## Runtime Validation
|
||||
|
||||
OpenAPI and generated files define the transport interface, but generated Go
|
||||
decoding is not a complete schema validator. In particular, ordinary struct
|
||||
decoding does not reject unknown JSON fields just because a schema says
|
||||
`additionalProperties: false`.
|
||||
|
||||
When a request schema relies on constraints that generated code does not enforce,
|
||||
choose one explicit validation strategy at the `api` seam:
|
||||
|
||||
- configure the generated decoder or runtime to enforce the constraint
|
||||
- validate the raw request body against the canonical schema before mapping it to
|
||||
generated types
|
||||
- add a narrow hand-written transport validator in `api` that checks only the
|
||||
missing generated-runtime guarantees
|
||||
|
||||
Do not push transport-shape validation into `service`. The service interface
|
||||
should receive transport-agnostic input that has already crossed the HTTP
|
||||
contract seam.
|
||||
|
||||
For `WorkbenchSnapshot*`, transport ownership still remains in `api`: HTTP
|
||||
decoding, route-specific validation strategy, and status-code mapping stay at
|
||||
the API seam even when host services consume the synchronized shared snapshot
|
||||
contract through `packages/workbench/service`.
|
||||
|
||||
Add route tests for schema constraints that matter to daemon behavior and are
|
||||
not obviously enforced by the generated runtime. Examples include unknown-field
|
||||
rejection, object closure, array size limits, and numeric min/max constraints.
|
||||
|
||||
## Tooling Rules
|
||||
|
||||
- run `pnpm generate:api` when the OpenAPI contract changes
|
||||
- run `pnpm check:api-generated` before finishing API contract changes; this
|
||||
also runs `pnpm check:agent-protocol-enums`, which fails when the closed
|
||||
agent turn/interaction enums in `tuttid.v1.yaml` drift from the
|
||||
`activity.updated` event schema
|
||||
(`packages/events/protocol/definitions/agent/activity.updated.event.json`)
|
||||
- run `pnpm sync:workbench-openapi-schema` after changing the shared workbench
|
||||
snapshot schema
|
||||
- run `pnpm check:workbench-openapi-schema` when you only need to check that
|
||||
the shared snapshot schema and OpenAPI component schemas are aligned
|
||||
- run `pnpm sync:workbench-go-contract` after changing shared workbench snapshot
|
||||
schema or limits that daemon validation depends on
|
||||
- run `pnpm check:workbench-go-contract` when you only need to check that the
|
||||
generated shared Go workbench snapshot contract is aligned with the shared
|
||||
snapshot sources
|
||||
- run `pnpm generate:event-protocol` after changing shared business event
|
||||
protocol schemas or event-definition files
|
||||
- run `pnpm check:event-protocol-generated` when you only need to check that
|
||||
the generated TypeScript and Go event protocol outputs are aligned with the
|
||||
shared event sources
|
||||
- keep generator versions pinned in repository-managed dependencies or Go tool directives
|
||||
|
||||
## Scope Guidance
|
||||
|
||||
Generate:
|
||||
|
||||
- path and method contracts
|
||||
- transport request and response models
|
||||
- server interfaces and route glue
|
||||
- shared TypeScript SDK surface
|
||||
|
||||
Do not generate:
|
||||
|
||||
- daemon business logic
|
||||
- daemon persistence adapters
|
||||
- Electron lifecycle code
|
||||
- renderer workflows
|
||||
- desktop IPC contracts that are not direct HTTP projections
|
||||
Reference in New Issue
Block a user