Skip to content

Storage

Dline does not keep its persistent data in VS Code’s ExtensionContext. It writes to three independent file roots instead. Every persistent value has one owning module; when changing storage, go through that owner rather than building paths or writing JSON yourself.

RootResolverOverrideDefault
State/data rootgetDlineDataDir()DLINE_DIR; the actual path is <DLINE_DIR>/data~/.dline/data
Resource homegetDlineHomePath()DLINE_HOME_DIR~/.dline
Documents rootgetDlineDocumentsPath()DLINE_DOCS_DIR, used as isdline under the system Documents directory

All three resolvers are in src/core/storage/documents-path.ts. DLINE_DIR and DLINE_HOME_DIR both default to locations under ~/.dline, but they are separate overrides. When DLINE_DOCS_DIR is set, Dline does not append another dline directory.

The system Documents directory is resolved by querying MyDocuments through PowerShell on Windows, running xdg-user-dir DOCUMENTS on Linux, and otherwise falling back to ~/Documents. At startup, warmupDocumentsPathCache() caches the result for synchronous callers. Tests that need full isolation should set all three environment variables.

<data-root>/
├── globalState.json # non-setting global state
├── secrets.json # typed secrets managed by StateManager
├── settings/
│ ├── settings.json # global settings
│ └── api_profiles.json # Profile catalog
├── secrets/
│ ├── api_keys.json # API keys keyed by Profile ID
│ ├── provider_secrets.json # provider secrets keyed by Profile ID
│ └── ... # per-Profile OAuth credential files
└── workspaces/<hash>/
├── workspaceState.json # workspace state
└── settings.json # workspace settings

Secret files are created with owner-only permissions (0o600). Do not confuse settings/settings.json with settings/mcp_settings.json under the documents root.

<documents-root>/
├── tasks/
│ ├── taskHistory.db # task index (SQLite)
│ └── <task-id>/
├── rules/ workflows/ skills/ subagents/ mcp/ hooks/
├── settings/mcp_settings.json # MCP server settings
└── checkpoints/ # checkpoint shadow repositories

Workspace capability files are not stored here: rules, workflows, skills, subagents, and hooks live in the project’s .agents/rules, .agents/workflows, .agents/skills, .agents/subagents, and .agents/hooks, and workspace MCP descriptors live in .agents/mcp/.

The resource home holds provider metadata (providers/), caches (cache/, including the MCP Marketplace catalog cache), and Puppeteer data (puppeteer/).

All of these are under <documents-root>/tasks/<task-id>/:

FileOwner
journal.dbTask runtime commits: journal entries, command publications, and payloads; the conversation sent to the model is rebuilt from it
state.json, state.json.bakThe durable kernel snapshot used to reopen and recover the task; the previous version is kept as .bak
messages.dbUIMessage, the chat projection shown in the Webview
activities.dbTaskActivityPersistence, task activity events
metrics.dbAPI rate metrics and request/response execution data
context.json, context_history.jsonlTask context and context-management records
settings.jsonTask-level setting overrides
task_metadata.jsonTask metadata
subagents/<subagent-id>/One directory per subagent with the same layout, including subagents spawned by other subagents
artifacts/Task artifacts (such as generated images); only task-relative paths are allowed
tmp/command-logs/, shell-diagnostics/, image-previews/, image-viewer/

src/core/storage/instance/InstanceLayout.ts is the single source of these paths, and each database has exactly one writing module.

tmp/command-logs/ survives across sessions because chat rows and the Activities view link to those logs. It is bounded by a per-task total size budget (200 MiB by default) that drops the oldest logs first; age alone never deletes a log. api_rate_metrics.jsonl is a legacy format that is imported into metrics.db with fingerprint checks.

Deleting a task must go through the task deletion coordinator, which handles locks, active Controllers, panels, TaskHistory, database files, and checkpoints. When you add a task-owned file, decide who is responsible for it during deletion and recovery.

CategoryAuthorityConsistency
Non-setting global stateglobalState.jsonBatched writes with a 2-second debounce and an explicit flush
Global settingssettings/settings.json through SettingsRepositoryCross-process file lock; reads the latest revision inside the lock, writes a temporary file, then renames it atomically; watches for external changes
Workspace settingsA separate SettingsRepository for workspaces/<hash>/settings.jsonSame as global settings
Workspace stateworkspaces/<hash>/workspaceState.jsonBatched writes
Task settingstasks/<task-id>/settings.jsonRead, merge, write a temporary file, then rename atomically
Session overridesStateManager memory onlyNever persisted
Profile catalogsettings/api_profiles.json through ProfileCatalogRepositoryMerges this change, keyed by stable ID, into the latest version on disk inside a cross-process lock
Task indextaskHistory.db through TaskHistorySQLite WAL; watches both the database and the WAL file; queues metadata writes

StateManager.flushPendingState() keeps flushing until no pending changes remain; at shutdown it rejects new changes before the final flush.

Task-aware settings resolve in this order; the first hit wins:

  1. Remote configuration
  2. Session override
  3. Task setting
  4. Global setting
  5. Legacy fallback during migration
  6. Declared default

autoApprovalSettings is an exception: it reads only the global setting, so a historical task snapshot cannot override the current approval permissions.

Capability toggles (rules, workflows, skills, and so on) use three sparse scopes: global, workspace, and task, with the task scope strongest. A resource with no entry in a scope means “inherit”, not “disabled”. Use getScopedCapabilityToggles() and mutateScopedCapabilityToggles() to read and write a specific scope.

  • Cline data: Dline never reads or imports legacy Cline paths. Users copy configuration by hand; see Migrating from Cline.
  • Exporting VS Code storage: exportVSCodeStorageToSharedFiles() (in src/hosts/vscode/vscode-to-file-migration.ts) reads legacy ExtensionContext global state, workspace state, and secrets and writes them to file storage. Global state and each workspace record their version separately in __vscodeMigrationVersion; values already in files take precedence.
  • Settings migration: StateManager uses __settingsMigrationVersion to move supported legacy global-state settings into the settings file, and removes the legacy copies only after the settings file owns them.
  • Task history: the legacy taskHistory.jsonl is imported only when taskHistory.db does not exist. Once the database has been created, later launches do not attempt the import again.
  • Message and activity files: a legacy ui_messages.jsonl (or an older ui_messages.json/claude_messages.json) is imported into messages.db on first open, and activities.json into activities.db. Each file is renamed to <name>.migrated only after its rows are committed and read back, so an interrupted import is retried on the next open.
  • Metrics database: a legacy <task-id>.db and its WAL are moved to metrics.db once; an existing metrics.db is never overwritten.
  • Task runtime upgrade: snapshot.json from earlier versions is read only to import its input queue into state.json, once; the earlier completion state is not imported, so such a task may reopen as interrupted. The first time a task from an earlier version is continued, its api_conversation_history.jsonl is imported into journal.db while the journal holds nothing but that import; the file itself is left unchanged. A task opened by this version cannot be continued by an older one.
  1. Decide the owner, root, scope, consistency requirement, and cleanup path first.
  2. Add a key to src/shared/storage/state-keys.ts only when the value truly belongs to StateManager-managed global state, settings, workspace state, or secrets.
  3. Change user settings through the global or workspace SettingsRepository; use the scoped APIs for capability toggles.
  4. Carry a user-visible setting through the full Proto, Controller, and Webview round trip; a field added only to in-memory state is not persisted.
  5. Define a finite flush and close behavior for buffered writes, and invoke it at task, Controller, host shutdown, and recovery boundaries.
  6. Test restart recovery, concurrent windows, stale writers, workspace and task isolation, migration retries, malformed or partial files, and cleanup on deletion.