Include session titles in external agent import history (#35870)

## What changed

- Preserve the original title when importing an external-agent session and return it with successful session entries from `externalAgentConfig/import/readHistories`.
- Add dedicated history-record success types so externally completed imports can optionally supply a session title without changing the live import result type.

## Testing

- Verify imported session titles are returned by the app server and persisted when session import ledger entries are created or refreshed.

GitOrigin-RevId: e896688a5763c238f1e1dc8b4672a7aa7bc1ed4c
This commit is contained in:
charlesgong-openai authored and copyberry committed 2026-07-29 02:45:55 +00:00
1 parent 28f3f1f9ef
commit 9f23e97797
28 files changed
+465 -130

No files matched your search

+63 -55
View File
@@ -916,7 +916,7 @@
"itemTypeResults": { "itemTypeResults": {
"description": "Completed results grouped by imported item type.", "description": "Completed results grouped by imported item type.",
"items": { "items": {
"$ref": "#/definitions/ExternalAgentConfigImportTypeResult" "$ref": "#/definitions/ExternalAgentConfigImportHistoryRecordTypeResultParams"
}, },
"type": "array" "type": "array"
}, },
@@ -931,6 +931,68 @@
], ],
"type": "object" "type": "object"
}, },
"ExternalAgentConfigImportHistoryRecordSuccessParams": {
"properties": {
"cwd": {
"type": [
"string",
"null"
]
},
"itemType": {
"$ref": "#/definitions/ExternalAgentConfigMigrationItemType"
},
"source": {
"type": [
"string",
"null"
]
},
"target": {
"type": [
"string",
"null"
]
},
"title": {
"default": null,
"description": "Original title for an imported session, when available.",
"type": [
"string",
"null"
]
}
},
"required": [
"itemType"
],
"type": "object"
},
"ExternalAgentConfigImportHistoryRecordTypeResultParams": {
"properties": {
"failures": {
"items": {
"$ref": "#/definitions/ExternalAgentConfigImportItemTypeFailure"
},
"type": "array"
},
"itemType": {
"$ref": "#/definitions/ExternalAgentConfigMigrationItemType"
},
"successes": {
"items": {
"$ref": "#/definitions/ExternalAgentConfigImportHistoryRecordSuccessParams"
},
"type": "array"
}
},
"required": [
"failures",
"itemType",
"successes"
],
"type": "object"
},
"ExternalAgentConfigImportItemTypeFailure": { "ExternalAgentConfigImportItemTypeFailure": {
"properties": { "properties": {
"cwd": { "cwd": {
@@ -974,35 +1036,6 @@
], ],
"type": "object" "type": "object"
}, },
"ExternalAgentConfigImportItemTypeSuccess": {
"properties": {
"cwd": {
"type": [
"string",
"null"
]
},
"itemType": {
"$ref": "#/definitions/ExternalAgentConfigMigrationItemType"
},
"source": {
"type": [
"string",
"null"
]
},
"target": {
"type": [
"string",
"null"
]
}
},
"required": [
"itemType"
],
"type": "object"
},
"ExternalAgentConfigImportParams": { "ExternalAgentConfigImportParams": {
"properties": { "properties": {
"migrationItems": { "migrationItems": {
@@ -1038,31 +1071,6 @@
], ],
"type": "object" "type": "object"
}, },
"ExternalAgentConfigImportTypeResult": {
"properties": {
"failures": {
"items": {
"$ref": "#/definitions/ExternalAgentConfigImportItemTypeFailure"
},
"type": "array"
},
"itemType": {
"$ref": "#/definitions/ExternalAgentConfigMigrationItemType"
},
"successes": {
"items": {
"$ref": "#/definitions/ExternalAgentConfigImportItemTypeSuccess"
},
"type": "array"
}
},
"required": [
"failures",
"itemType",
"successes"
],
"type": "object"
},
"ExternalAgentConfigMigrationItem": { "ExternalAgentConfigMigrationItem": {
"properties": { "properties": {
"cwd": { "cwd": {
@@ -1275,6 +1275,14 @@
"string", "string",
"null" "null"
] ]
},
"title": {
"default": null,
"description": "Original title for an imported session; null for other item types.",
"type": [
"string",
"null"
]
} }
}, },
"required": [ "required": [
@@ -9837,7 +9837,7 @@
"itemTypeResults": { "itemTypeResults": {
"description": "Completed results grouped by imported item type.", "description": "Completed results grouped by imported item type.",
"items": { "items": {
"$ref": "#/definitions/v2/ExternalAgentConfigImportTypeResult" "$ref": "#/definitions/v2/ExternalAgentConfigImportHistoryRecordTypeResultParams"
}, },
"type": "array" "type": "array"
}, },
@@ -9866,6 +9866,68 @@
"title": "ExternalAgentConfigImportHistoryRecordResponse", "title": "ExternalAgentConfigImportHistoryRecordResponse",
"type": "object" "type": "object"
}, },
"ExternalAgentConfigImportHistoryRecordSuccessParams": {
"properties": {
"cwd": {
"type": [
"string",
"null"
]
},
"itemType": {
"$ref": "#/definitions/v2/ExternalAgentConfigMigrationItemType"
},
"source": {
"type": [
"string",
"null"
]
},
"target": {
"type": [
"string",
"null"
]
},
"title": {
"default": null,
"description": "Original title for an imported session, when available.",
"type": [
"string",
"null"
]
}
},
"required": [
"itemType"
],
"type": "object"
},
"ExternalAgentConfigImportHistoryRecordTypeResultParams": {
"properties": {
"failures": {
"items": {
"$ref": "#/definitions/v2/ExternalAgentConfigImportItemTypeFailure"
},
"type": "array"
},
"itemType": {
"$ref": "#/definitions/v2/ExternalAgentConfigMigrationItemType"
},
"successes": {
"items": {
"$ref": "#/definitions/v2/ExternalAgentConfigImportHistoryRecordSuccessParams"
},
"type": "array"
}
},
"required": [
"failures",
"itemType",
"successes"
],
"type": "object"
},
"ExternalAgentConfigImportItemTypeFailure": { "ExternalAgentConfigImportItemTypeFailure": {
"properties": { "properties": {
"cwd": { "cwd": {
@@ -9931,6 +9993,14 @@
"string", "string",
"null" "null"
] ]
},
"title": {
"default": null,
"description": "Original title for an imported session; null for other item types.",
"type": [
"string",
"null"
]
} }
}, },
"required": [ "required": [
@@ -6015,7 +6015,7 @@
"itemTypeResults": { "itemTypeResults": {
"description": "Completed results grouped by imported item type.", "description": "Completed results grouped by imported item type.",
"items": { "items": {
"$ref": "#/definitions/ExternalAgentConfigImportTypeResult" "$ref": "#/definitions/ExternalAgentConfigImportHistoryRecordTypeResultParams"
}, },
"type": "array" "type": "array"
}, },
@@ -6044,6 +6044,68 @@
"title": "ExternalAgentConfigImportHistoryRecordResponse", "title": "ExternalAgentConfigImportHistoryRecordResponse",
"type": "object" "type": "object"
}, },
"ExternalAgentConfigImportHistoryRecordSuccessParams": {
"properties": {
"cwd": {
"type": [
"string",
"null"
]
},
"itemType": {
"$ref": "#/definitions/ExternalAgentConfigMigrationItemType"
},
"source": {
"type": [
"string",
"null"
]
},
"target": {
"type": [
"string",
"null"
]
},
"title": {
"default": null,
"description": "Original title for an imported session, when available.",
"type": [
"string",
"null"
]
}
},
"required": [
"itemType"
],
"type": "object"
},
"ExternalAgentConfigImportHistoryRecordTypeResultParams": {
"properties": {
"failures": {
"items": {
"$ref": "#/definitions/ExternalAgentConfigImportItemTypeFailure"
},
"type": "array"
},
"itemType": {
"$ref": "#/definitions/ExternalAgentConfigMigrationItemType"
},
"successes": {
"items": {
"$ref": "#/definitions/ExternalAgentConfigImportHistoryRecordSuccessParams"
},
"type": "array"
}
},
"required": [
"failures",
"itemType",
"successes"
],
"type": "object"
},
"ExternalAgentConfigImportItemTypeFailure": { "ExternalAgentConfigImportItemTypeFailure": {
"properties": { "properties": {
"cwd": { "cwd": {
@@ -6109,6 +6171,14 @@
"string", "string",
"null" "null"
] ]
},
"title": {
"default": null,
"description": "Original title for an imported session; null for other item types.",
"type": [
"string",
"null"
]
} }
}, },
"required": [ "required": [
@@ -66,6 +66,14 @@
"string", "string",
"null" "null"
] ]
},
"title": {
"default": null,
"description": "Original title for an imported session; null for other item types.",
"type": [
"string",
"null"
]
} }
}, },
"required": [ "required": [
@@ -102,6 +102,14 @@
"string", "string",
"null" "null"
] ]
},
"title": {
"default": null,
"description": "Original title for an imported session; null for other item types.",
"type": [
"string",
"null"
]
} }
}, },
"required": [ "required": [
@@ -1,6 +1,68 @@
{ {
"$schema": "http://json-schema.org/draft-07/schema#", "$schema": "http://json-schema.org/draft-07/schema#",
"definitions": { "definitions": {
"ExternalAgentConfigImportHistoryRecordSuccessParams": {
"properties": {
"cwd": {
"type": [
"string",
"null"
]
},
"itemType": {
"$ref": "#/definitions/ExternalAgentConfigMigrationItemType"
},
"source": {
"type": [
"string",
"null"
]
},
"target": {
"type": [
"string",
"null"
]
},
"title": {
"default": null,
"description": "Original title for an imported session, when available.",
"type": [
"string",
"null"
]
}
},
"required": [
"itemType"
],
"type": "object"
},
"ExternalAgentConfigImportHistoryRecordTypeResultParams": {
"properties": {
"failures": {
"items": {
"$ref": "#/definitions/ExternalAgentConfigImportItemTypeFailure"
},
"type": "array"
},
"itemType": {
"$ref": "#/definitions/ExternalAgentConfigMigrationItemType"
},
"successes": {
"items": {
"$ref": "#/definitions/ExternalAgentConfigImportHistoryRecordSuccessParams"
},
"type": "array"
}
},
"required": [
"failures",
"itemType",
"successes"
],
"type": "object"
},
"ExternalAgentConfigImportItemTypeFailure": { "ExternalAgentConfigImportItemTypeFailure": {
"properties": { "properties": {
"cwd": { "cwd": {
@@ -44,60 +106,6 @@
], ],
"type": "object" "type": "object"
}, },
"ExternalAgentConfigImportItemTypeSuccess": {
"properties": {
"cwd": {
"type": [
"string",
"null"
]
},
"itemType": {
"$ref": "#/definitions/ExternalAgentConfigMigrationItemType"
},
"source": {
"type": [
"string",
"null"
]
},
"target": {
"type": [
"string",
"null"
]
}
},
"required": [
"itemType"
],
"type": "object"
},
"ExternalAgentConfigImportTypeResult": {
"properties": {
"failures": {
"items": {
"$ref": "#/definitions/ExternalAgentConfigImportItemTypeFailure"
},
"type": "array"
},
"itemType": {
"$ref": "#/definitions/ExternalAgentConfigMigrationItemType"
},
"successes": {
"items": {
"$ref": "#/definitions/ExternalAgentConfigImportItemTypeSuccess"
},
"type": "array"
}
},
"required": [
"failures",
"itemType",
"successes"
],
"type": "object"
},
"ExternalAgentConfigMigrationItemType": { "ExternalAgentConfigMigrationItemType": {
"enum": [ "enum": [
"AGENTS_MD", "AGENTS_MD",
@@ -118,7 +126,7 @@
"itemTypeResults": { "itemTypeResults": {
"description": "Completed results grouped by imported item type.", "description": "Completed results grouped by imported item type.",
"items": { "items": {
"$ref": "#/definitions/ExternalAgentConfigImportTypeResult" "$ref": "#/definitions/ExternalAgentConfigImportHistoryRecordTypeResultParams"
}, },
"type": "array" "type": "array"
}, },
@@ -66,6 +66,14 @@
"string", "string",
"null" "null"
] ]
},
"title": {
"default": null,
"description": "Original title for an imported session; null for other item types.",
"type": [
"string",
"null"
]
} }
}, },
"required": [ "required": [
@@ -1,7 +1,7 @@
// GENERATED CODE! DO NOT MODIFY BY HAND! // GENERATED CODE! DO NOT MODIFY BY HAND!
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
import type { ExternalAgentConfigImportTypeResult } from "./ExternalAgentConfigImportTypeResult"; import type { ExternalAgentConfigImportHistoryRecordTypeResultParams } from "./ExternalAgentConfigImportHistoryRecordTypeResultParams";
export type ExternalAgentConfigImportHistoryRecordParams = { export type ExternalAgentConfigImportHistoryRecordParams = {
/** /**
@@ -11,4 +11,4 @@ providerId: string,
/** /**
* Completed results grouped by imported item type. * Completed results grouped by imported item type.
*/ */
itemTypeResults: Array<ExternalAgentConfigImportTypeResult>, }; itemTypeResults: Array<ExternalAgentConfigImportHistoryRecordTypeResultParams>, };
@@ -0,0 +1,10 @@
// GENERATED CODE! DO NOT MODIFY BY HAND!
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
import type { ExternalAgentConfigMigrationItemType } from "./ExternalAgentConfigMigrationItemType";
export type ExternalAgentConfigImportHistoryRecordSuccessParams = { itemType: ExternalAgentConfigMigrationItemType, cwd: string | null, source: string | null, target: string | null,
/**
* Original title for an imported session, when available.
*/
title?: string | null, };
@@ -0,0 +1,8 @@
// GENERATED CODE! DO NOT MODIFY BY HAND!
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
import type { ExternalAgentConfigImportHistoryRecordSuccessParams } from "./ExternalAgentConfigImportHistoryRecordSuccessParams";
import type { ExternalAgentConfigImportItemTypeFailure } from "./ExternalAgentConfigImportItemTypeFailure";
import type { ExternalAgentConfigMigrationItemType } from "./ExternalAgentConfigMigrationItemType";
export type ExternalAgentConfigImportHistoryRecordTypeResultParams = { itemType: ExternalAgentConfigMigrationItemType, successes: Array<ExternalAgentConfigImportHistoryRecordSuccessParams>, failures: Array<ExternalAgentConfigImportItemTypeFailure>, };
@@ -3,4 +3,8 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
import type { ExternalAgentConfigMigrationItemType } from "./ExternalAgentConfigMigrationItemType"; import type { ExternalAgentConfigMigrationItemType } from "./ExternalAgentConfigMigrationItemType";
export type ExternalAgentConfigImportItemTypeSuccess = { itemType: ExternalAgentConfigMigrationItemType, cwd: string | null, source: string | null, target: string | null, }; export type ExternalAgentConfigImportItemTypeSuccess = { itemType: ExternalAgentConfigMigrationItemType, cwd: string | null, source: string | null, target: string | null,
/**
* Original title for an imported session; null for other item types.
*/
title: string | null, };
@@ -122,6 +122,8 @@ export type { ExternalAgentConfigImportHistoriesReadResponse } from "./ExternalA
export type { ExternalAgentConfigImportHistory } from "./ExternalAgentConfigImportHistory"; export type { ExternalAgentConfigImportHistory } from "./ExternalAgentConfigImportHistory";
export type { ExternalAgentConfigImportHistoryRecordParams } from "./ExternalAgentConfigImportHistoryRecordParams"; export type { ExternalAgentConfigImportHistoryRecordParams } from "./ExternalAgentConfigImportHistoryRecordParams";
export type { ExternalAgentConfigImportHistoryRecordResponse } from "./ExternalAgentConfigImportHistoryRecordResponse"; export type { ExternalAgentConfigImportHistoryRecordResponse } from "./ExternalAgentConfigImportHistoryRecordResponse";
export type { ExternalAgentConfigImportHistoryRecordSuccessParams } from "./ExternalAgentConfigImportHistoryRecordSuccessParams";
export type { ExternalAgentConfigImportHistoryRecordTypeResultParams } from "./ExternalAgentConfigImportHistoryRecordTypeResultParams";
export type { ExternalAgentConfigImportItemTypeFailure } from "./ExternalAgentConfigImportItemTypeFailure"; export type { ExternalAgentConfigImportItemTypeFailure } from "./ExternalAgentConfigImportItemTypeFailure";
export type { ExternalAgentConfigImportItemTypeSuccess } from "./ExternalAgentConfigImportItemTypeSuccess"; export type { ExternalAgentConfigImportItemTypeSuccess } from "./ExternalAgentConfigImportItemTypeSuccess";
export type { ExternalAgentConfigImportParams } from "./ExternalAgentConfigImportParams"; export type { ExternalAgentConfigImportParams } from "./ExternalAgentConfigImportParams";
@@ -787,6 +787,9 @@ pub struct ExternalAgentConfigImportItemTypeSuccess {
pub cwd: Option<PathBuf>, pub cwd: Option<PathBuf>,
pub source: Option<String>, pub source: Option<String>,
pub target: Option<String>, pub target: Option<String>,
/// Original title for an imported session; null for other item types.
#[serde(default)]
pub title: Option<String>,
} }
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema, TS)] #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema, TS)]
@@ -798,6 +801,29 @@ pub struct ExternalAgentConfigImportTypeResult {
pub failures: Vec<ExternalAgentConfigImportItemTypeFailure>, pub failures: Vec<ExternalAgentConfigImportItemTypeFailure>,
} }
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema, TS)]
#[serde(rename_all = "camelCase")]
#[ts(export_to = "v2/")]
pub struct ExternalAgentConfigImportHistoryRecordSuccessParams {
pub item_type: ExternalAgentConfigMigrationItemType,
pub cwd: Option<PathBuf>,
pub source: Option<String>,
pub target: Option<String>,
/// Original title for an imported session, when available.
#[serde(default)]
#[ts(optional = nullable)]
pub title: Option<String>,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema, TS)]
#[serde(rename_all = "camelCase")]
#[ts(export_to = "v2/")]
pub struct ExternalAgentConfigImportHistoryRecordTypeResultParams {
pub item_type: ExternalAgentConfigMigrationItemType,
pub successes: Vec<ExternalAgentConfigImportHistoryRecordSuccessParams>,
pub failures: Vec<ExternalAgentConfigImportItemTypeFailure>,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema, TS)] #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema, TS)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
#[ts(export_to = "v2/")] #[ts(export_to = "v2/")]
@@ -805,7 +831,7 @@ pub struct ExternalAgentConfigImportHistoryRecordParams {
/// Opaque provider identifier for the externally completed import. /// Opaque provider identifier for the externally completed import.
pub provider_id: String, pub provider_id: String,
/// Completed results grouped by imported item type. /// Completed results grouped by imported item type.
pub item_type_results: Vec<ExternalAgentConfigImportTypeResult>, pub item_type_results: Vec<ExternalAgentConfigImportHistoryRecordTypeResultParams>,
} }
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema, TS)] #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema, TS)]
+1 -1
View File
@@ -248,7 +248,7 @@ Example with notification opt-out:
- `config/read` — fetch the runtime-effective config after resolving config layering and managed requirements, including opaque `desktop` values stored in `config.toml`. - `config/read` — fetch the runtime-effective config after resolving config layering and managed requirements, including opaque `desktop` values stored in `config.toml`.
- `externalAgentConfig/detect` — detect migratable external-agent artifacts with `includeHome`, optional `cwds`, and an optional `migrationSource` selector. Omitted, `null`, or unrecognized migration-source values retain the default behavior. The deprecated optional `source` field remains accepted for compatibility but does not select the migration source. Each detected item includes `cwd` (`null` for home), and multi-item migrations may additionally include structured `details` with plugin ids, skill names, memory, session metadata, or other artifact names. - `externalAgentConfig/detect` — detect migratable external-agent artifacts with `includeHome`, optional `cwds`, and an optional `migrationSource` selector. Omitted, `null`, or unrecognized migration-source values retain the default behavior. The deprecated optional `source` field remains accepted for compatibility but does not select the migration source. Each detected item includes `cwd` (`null` for home), and multi-item migrations may additionally include structured `details` with plugin ids, skill names, memory, session metadata, or other artifact names.
- `externalAgentConfig/import` — apply selected external-agent migration items by passing explicit `migrationItems` with `cwd` (`null` for home) and any `details` returned by detect. Pass the same optional `migrationSource` used for detection so the server reads from the matching source; omitted, `null`, or unrecognized values retain the default behavior. The optional `source` identifies the product that initiated the import, while the optional opaque `providerId` attributes analytics to the provider selected by that product without affecting migration-source selection. The response acknowledges the synchronous import phase with an `importId`. Expected migration failures are reported as per-item failures rather than JSON-RPC errors, so the server still returns that `importId` and emits `externalAgentConfig/import/completed` with the same ID once all synchronous and background work finishes. The completion notification contains type-level `itemTypeResults` with successes and failures, including raw failure messages for the client to report separately. - `externalAgentConfig/import` — apply selected external-agent migration items by passing explicit `migrationItems` with `cwd` (`null` for home) and any `details` returned by detect. Pass the same optional `migrationSource` used for detection so the server reads from the matching source; omitted, `null`, or unrecognized values retain the default behavior. The optional `source` identifies the product that initiated the import, while the optional opaque `providerId` attributes analytics to the provider selected by that product without affecting migration-source selection. The response acknowledges the synchronous import phase with an `importId`. Expected migration failures are reported as per-item failures rather than JSON-RPC errors, so the server still returns that `importId` and emits `externalAgentConfig/import/completed` with the same ID once all synchronous and background work finishes. The completion notification contains type-level `itemTypeResults` with successes and failures, including raw failure messages for the client to report separately.
- `externalAgentConfig/import/readHistories` — read completed import histories and connector candidates detected from successfully imported session histories. Connector candidates include a normalized display `name`, the number of imported sessions that used the connector, and the source metadata field used for detection. - `externalAgentConfig/import/readHistories` — read completed import histories and connector candidates detected from successfully imported session histories. Successful session entries include the original imported title when one was available. Connector candidates include a normalized display `name`, the number of imported sessions that used the connector, and the source metadata field used for detection.
- `config/value/write` — write a single config key/value to the user's config.toml on disk; dotted paths such as `desktop.someKey` use the same generic write surface. Writes that overlap a managed requirement are rejected with `configRequirementReadonly`. - `config/value/write` — write a single config key/value to the user's config.toml on disk; dotted paths such as `desktop.someKey` use the same generic write surface. Writes that overlap a managed requirement are rejected with `configRequirementReadonly`.
- `config/batchWrite` — apply multiple config edits atomically to the user's config.toml on disk, with optional `reloadUserConfig: true` to hot-reload loaded threads, including multiple `desktop.*` edits. Session-static model, reasoning-effort, Plan-mode reasoning-effort, service-tier, and personality defaults do not reload existing threads. - `config/batchWrite` — apply multiple config edits atomically to the user's config.toml on disk, with optional `reloadUserConfig: true` to hot-reload loaded threads, including multiple `desktop.*` edits. Session-static model, reasoning-effort, Plan-mode reasoning-effort, service-tier, and personality defaults do not reload existing threads.
- `configRequirements/read` — fetch loaded requirements constraints from `requirements.toml` and/or MDM (or `null` if none are configured), including exact managed values (`sqliteHome`, `logDir`, `modelCatalogJson`, `checkForUpdateOnStartup`, `allowLoginShell`, `feedback.enabled`, and `windowsSandboxPrivateDesktop`), allow-lists (`allowedApprovalPolicies`, `allowedSandboxModes`, `allowedWebSearchModes`), the layered permission-profile allow map (`allowedPermissionProfiles`), the managed permission-profile default (`defaultPermissions`), lifecycle hook lockdown (`allowManagedHooksOnly`), remote-control policy (`allowRemoteControl`; `false` force-disables remote control while `true` or `null` preserves existing behavior), computer use policy (`computerUse`), Browser Use policy (`browserUse.disableAutoReview`), pinned feature values (`featureRequirements`, including the default-allowed `in_app_updates` policy that administrators can set to `false`), managed lifecycle hooks (`hooks`, including each command handler's optional `additionalContextLimit`), `enforceResidency`, managed new-thread defaults (`models.newThread.model`, `models.newThread.modelReasoningEffort`, and `models.newThread.serviceTier`), and `network` constraints such as canonical domain/socket permissions plus `managedAllowedDomainsOnly` and `dangerFullAccessDenylistOnly`. - `configRequirements/read` — fetch loaded requirements constraints from `requirements.toml` and/or MDM (or `null` if none are configured), including exact managed values (`sqliteHome`, `logDir`, `modelCatalogJson`, `checkForUpdateOnStartup`, `allowLoginShell`, `feedback.enabled`, and `windowsSandboxPrivateDesktop`), allow-lists (`allowedApprovalPolicies`, `allowedSandboxModes`, `allowedWebSearchModes`), the layered permission-profile allow map (`allowedPermissionProfiles`), the managed permission-profile default (`defaultPermissions`), lifecycle hook lockdown (`allowManagedHooksOnly`), remote-control policy (`allowRemoteControl`; `false` force-disables remote control while `true` or `null` preserves existing behavior), computer use policy (`computerUse`), Browser Use policy (`browserUse.disableAutoReview`), pinned feature values (`featureRequirements`, including the default-allowed `in_app_updates` policy that administrators can set to `false`), managed lifecycle hooks (`hooks`, including each command handler's optional `additionalContextLimit`), `enforceResidency`, managed new-thread defaults (`models.newThread.model`, `models.newThread.modelReasoningEffort`, and `models.newThread.serviceTier`), and `network` constraints such as canonical domain/socket permissions plus `managedAllowedDomainsOnly` and `dangerFullAccessDenylistOnly`.
@@ -17,6 +17,7 @@ use codex_app_server_protocol::ExternalAgentConfigImportHistoriesReadResponse;
use codex_app_server_protocol::ExternalAgentConfigImportHistoryRecordParams; use codex_app_server_protocol::ExternalAgentConfigImportHistoryRecordParams;
use codex_app_server_protocol::ExternalAgentConfigImportHistoryRecordResponse; use codex_app_server_protocol::ExternalAgentConfigImportHistoryRecordResponse;
use codex_app_server_protocol::ExternalAgentConfigImportItemTypeFailure as ProtocolImportFailure; use codex_app_server_protocol::ExternalAgentConfigImportItemTypeFailure as ProtocolImportFailure;
use codex_app_server_protocol::ExternalAgentConfigImportItemTypeSuccess as ProtocolImportSuccess;
use codex_app_server_protocol::ExternalAgentConfigImportParams; use codex_app_server_protocol::ExternalAgentConfigImportParams;
use codex_app_server_protocol::ExternalAgentConfigImportProgressNotification; use codex_app_server_protocol::ExternalAgentConfigImportProgressNotification;
use codex_app_server_protocol::ExternalAgentConfigImportResponse; use codex_app_server_protocol::ExternalAgentConfigImportResponse;
@@ -390,11 +391,30 @@ impl ExternalAgentConfigRequestProcessor {
.as_ref() .as_ref()
.ok_or_else(|| internal_error("state database is unavailable"))?; .ok_or_else(|| internal_error("state database is unavailable"))?;
let import_id = Uuid::new_v4().to_string(); let import_id = Uuid::new_v4().to_string();
let item_type_results = params
.item_type_results
.into_iter()
.map(|type_result| ProtocolImportTypeResult {
item_type: type_result.item_type,
successes: type_result
.successes
.into_iter()
.map(|success| ProtocolImportSuccess {
item_type: success.item_type,
cwd: success.cwd,
source: success.source,
target: success.target,
title: success.title,
})
.collect(),
failures: type_result.failures,
})
.collect::<Vec<_>>();
record_import_history( record_import_history(
state_db, state_db,
import_id.as_str(), import_id.as_str(),
Some(params.provider_id.as_str()), Some(params.provider_id.as_str()),
&params.item_type_results, &item_type_results,
) )
.await .await
.map_err(|err| internal_error(format!("failed to record import history: {err}")))?; .map_err(|err| internal_error(format!("failed to record import history: {err}")))?;
@@ -639,6 +659,7 @@ async fn record_import_history(
cwd: success.cwd.clone(), cwd: success.cwd.clone(),
source: success.source.clone(), source: success.source.clone(),
target: success.target.clone(), target: success.target.clone(),
title: success.title.clone(),
}) })
}) })
.collect::<anyhow::Result<Vec<_>>>()?; .collect::<anyhow::Result<Vec<_>>>()?;
@@ -672,7 +693,11 @@ fn apply_plugin_outcome_to_item_result(
plugin_outcome: PluginImportOutcome, plugin_outcome: PluginImportOutcome,
) { ) {
for plugin_id in plugin_outcome.succeeded_plugin_ids { for plugin_id in plugin_outcome.succeeded_plugin_ids {
item_result.record_success(Some(plugin_id.clone()), Some(plugin_id)); item_result.record_success(
Some(plugin_id.clone()),
Some(plugin_id),
/*title*/ None,
);
} }
for raw_error in plugin_outcome.raw_errors { for raw_error in plugin_outcome.raw_errors {
item_result.record_error(raw_error); item_result.record_error(raw_error);
@@ -225,6 +225,7 @@ fn protocol_import_success_record(
cwd: record.cwd, cwd: record.cwd,
source: record.source, source: record.source,
target: record.target, target: record.target,
title: record.title,
}) })
} }
@@ -326,6 +327,7 @@ fn protocol_import_success(success: &ExternalAgentConfigImportSuccess) -> Protoc
cwd: success.cwd.clone(), cwd: success.cwd.clone(),
source: success.source.clone(), source: success.source.clone(),
target: success.target.clone(), target: success.target.clone(),
title: success.title.clone(),
} }
} }
@@ -113,6 +113,7 @@ impl ExternalAgentSessionImporter {
item_result.record_success( item_result.record_success(
Some(completed_import.import.source_path.display().to_string()), Some(completed_import.import.source_path.display().to_string()),
Some(completed_import.import.imported_thread_id.to_string()), Some(completed_import.import.imported_thread_id.to_string()),
completed_import.import.title.clone(),
); );
completed_imports.push(completed_import); completed_imports.push(completed_import);
} }
@@ -211,6 +212,7 @@ impl ExternalAgentSessionImporter {
session_id: session_id.to_string(), session_id: session_id.to_string(),
server_ids: pending_import.attributed_mcp_server_ids, server_ids: pending_import.attributed_mcp_server_ids,
}); });
let title = pending_import.session.title.clone();
let imported_thread_id = let imported_thread_id =
self.persist_session(pending_import.session) self.persist_session(pending_import.session)
.await .await
@@ -226,6 +228,7 @@ impl ExternalAgentSessionImporter {
source_content_sha256: pending_import.source_content_sha256, source_content_sha256: pending_import.source_content_sha256,
imported_thread_id, imported_thread_id,
connector_names: Vec::new(), connector_names: Vec::new(),
title,
}, },
connector_attribution, connector_attribution,
})) }))
@@ -654,6 +654,7 @@ async fn external_agent_config_records_externally_completed_import_history() ->
"cwd": "/repo", "cwd": "/repo",
"source": "/source/session.jsonl", "source": "/source/session.jsonl",
"target": "thread-1", "target": "thread-1",
"title": null,
}]) }])
); );
assert_eq!(entry.failures, Vec::new()); assert_eq!(entry.failures, Vec::new());
@@ -1655,6 +1656,13 @@ async fn external_agent_config_import_creates_session_rollouts() -> Result<()> {
source: ExternalAgentImportedConnectorSource::RemoteMcpServersConfig, source: ExternalAgentImportedConnectorSource::RemoteMcpServersConfig,
}] }]
); );
let imported_session = response
.data
.iter()
.flat_map(|history| history.successes.iter())
.find(|success| success.item_type == ExternalAgentConfigMigrationItemType::Sessions)
.expect("imported session history should be available");
assert_eq!(imported_session.title.as_deref(), Some("Fix auth flow"));
let request_id = mcp let request_id = mcp
.send_thread_list_request(ThreadListParams { .send_thread_list_request(ThreadListParams {
@@ -122,13 +122,19 @@ impl ExternalAgentConfigImportItemResult {
self.raw_errors.push(raw_error); self.raw_errors.push(raw_error);
} }
pub fn record_success(&mut self, source: Option<String>, target: Option<String>) { pub fn record_success(
&mut self,
source: Option<String>,
target: Option<String>,
title: Option<String>,
) {
self.success_count = self.success_count.saturating_add(1); self.success_count = self.success_count.saturating_add(1);
self.successes.push(ExternalAgentConfigImportSuccess { self.successes.push(ExternalAgentConfigImportSuccess {
item_type: self.item_type, item_type: self.item_type,
cwd: self.cwd.clone(), cwd: self.cwd.clone(),
source, source,
target, target,
title,
}); });
} }
} }
@@ -139,6 +145,7 @@ pub struct ExternalAgentConfigImportSuccess {
pub cwd: Option<PathBuf>, pub cwd: Option<PathBuf>,
pub source: Option<String>, pub source: Option<String>,
pub target: Option<String>, pub target: Option<String>,
pub title: Option<String>,
} }
#[derive(Debug, Clone, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq)]
@@ -173,7 +173,7 @@ impl ExternalAgentConfigService {
if let Some((source, target)) = if let Some((source, target)) =
self.import_config(migration_item.cwd.as_deref())? self.import_config(migration_item.cwd.as_deref())?
{ {
item_result.record_success(Some(source), Some(target)); item_result.record_success(Some(source), Some(target), /*title*/ None);
} }
emit_migration_metric( emit_migration_metric(
EXTERNAL_AGENT_CONFIG_IMPORT_METRIC, EXTERNAL_AGENT_CONFIG_IMPORT_METRIC,
@@ -190,7 +190,11 @@ impl ExternalAgentConfigService {
Some(imported_skills.len()), Some(imported_skills.len()),
); );
for skill_name in imported_skills { for skill_name in imported_skills {
item_result.record_success(Some(skill_name.clone()), Some(skill_name)); item_result.record_success(
Some(skill_name.clone()),
Some(skill_name),
/*title*/ None,
);
} }
Ok(()) Ok(())
})(), })(),
@@ -198,7 +202,7 @@ impl ExternalAgentConfigService {
if let Some((source, target)) = if let Some((source, target)) =
self.import_agents_md(migration_item.cwd.as_deref())? self.import_agents_md(migration_item.cwd.as_deref())?
{ {
item_result.record_success(Some(source), Some(target)); item_result.record_success(Some(source), Some(target), /*title*/ None);
} }
emit_migration_metric( emit_migration_metric(
EXTERNAL_AGENT_CONFIG_IMPORT_METRIC, EXTERNAL_AGENT_CONFIG_IMPORT_METRIC,
@@ -260,8 +264,11 @@ impl ExternalAgentConfigService {
} }
}; };
for plugin_id in plugin_outcome.succeeded_plugin_ids { for plugin_id in plugin_outcome.succeeded_plugin_ids {
item_result item_result.record_success(
.record_success(Some(plugin_id.clone()), Some(plugin_id)); Some(plugin_id.clone()),
Some(plugin_id),
/*title*/ None,
);
} }
for raw_error in plugin_outcome.raw_errors { for raw_error in plugin_outcome.raw_errors {
item_result.record_error(raw_error); item_result.record_error(raw_error);
@@ -292,7 +299,11 @@ impl ExternalAgentConfigService {
/*skills_count*/ None, /*skills_count*/ None,
); );
for server_name in migrated_server_names { for server_name in migrated_server_names {
item_result.record_success(Some(server_name.clone()), Some(server_name)); item_result.record_success(
Some(server_name.clone()),
Some(server_name),
/*title*/ None,
);
} }
Ok(()) Ok(())
})(), })(),
@@ -305,8 +316,11 @@ impl ExternalAgentConfigService {
Some(imported_subagents.len()), Some(imported_subagents.len()),
); );
for subagent_name in imported_subagents { for subagent_name in imported_subagents {
item_result item_result.record_success(
.record_success(Some(subagent_name.clone()), Some(subagent_name)); Some(subagent_name.clone()),
Some(subagent_name),
/*title*/ None,
);
} }
Ok(()) Ok(())
})(), })(),
@@ -318,7 +332,11 @@ impl ExternalAgentConfigService {
/*skills_count*/ None, /*skills_count*/ None,
); );
for hook_name in migrated_hook_names { for hook_name in migrated_hook_names {
item_result.record_success(Some(hook_name.clone()), Some(hook_name)); item_result.record_success(
Some(hook_name.clone()),
Some(hook_name),
/*title*/ None,
);
} }
Ok(()) Ok(())
})(), })(),
@@ -330,7 +348,11 @@ impl ExternalAgentConfigService {
Some(imported_commands.len()), Some(imported_commands.len()),
); );
for command_name in imported_commands { for command_name in imported_commands {
item_result.record_success(Some(command_name.clone()), Some(command_name)); item_result.record_success(
Some(command_name.clone()),
Some(command_name),
/*title*/ None,
);
} }
Ok(()) Ok(())
})(), })(),
@@ -358,6 +380,7 @@ impl ExternalAgentConfigService {
item_result.record_success( item_result.record_success(
Some(project_key), Some(project_key),
Some(target_path.display().to_string()), Some(target_path.display().to_string()),
/*title*/ None,
); );
} }
for failure in memory_outcome.failures { for failure in memory_outcome.failures {
@@ -66,6 +66,7 @@ fn import_success(
cwd, cwd,
source: Some(source.into()), source: Some(source.into()),
target: Some(target.into()), target: Some(target.into()),
title: None,
} }
} }
@@ -390,6 +390,7 @@ async fn import_local_plugins_returns_completed_status() {
cwd: None, cwd: None,
source: Some("cloudflare@my-plugins".to_string()), source: Some("cloudflare@my-plugins".to_string()),
target: Some("cloudflare@my-plugins".to_string()), target: Some("cloudflare@my-plugins".to_string()),
title: None,
}], }],
raw_errors: Vec::new(), raw_errors: Vec::new(),
}] }]
@@ -31,6 +31,8 @@ struct ImportedExternalAgentSessionRecord {
source_modified_at: Option<i64>, source_modified_at: Option<i64>,
#[serde(default)] #[serde(default)]
connector_names: Vec<String>, connector_names: Vec<String>,
#[serde(default)]
title: Option<String>,
} }
#[derive(Debug, PartialEq, Eq)] #[derive(Debug, PartialEq, Eq)]
@@ -39,6 +41,7 @@ pub struct CompletedExternalAgentSessionImport {
pub source_content_sha256: String, pub source_content_sha256: String,
pub imported_thread_id: ThreadId, pub imported_thread_id: ThreadId,
pub connector_names: Vec<String>, pub connector_names: Vec<String>,
pub title: Option<String>,
} }
#[derive(Debug, Clone, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq)]
@@ -74,6 +77,7 @@ pub(crate) fn record_imported_session(
source_path, source_path,
imported_thread_id, imported_thread_id,
connector_names: Vec::new(), connector_names: Vec::new(),
title: None,
}], }],
) )
} }
@@ -98,6 +102,7 @@ pub fn record_completed_session_imports(
record.imported_at = imported_at; record.imported_at = imported_at;
record.source_modified_at = source_modified_at.or(record.source_modified_at); record.source_modified_at = source_modified_at.or(record.source_modified_at);
record.connector_names = import.connector_names; record.connector_names = import.connector_names;
record.title = import.title;
ledger.records.push(record); ledger.records.push(record);
continue; continue;
} }
@@ -108,6 +113,7 @@ pub fn record_completed_session_imports(
imported_at, imported_at,
source_modified_at, source_modified_at,
connector_names: import.connector_names, connector_names: import.connector_names,
title: import.title,
}); });
} }
save_import_ledger(codex_home, &ledger) save_import_ledger(codex_home, &ledger)
@@ -38,6 +38,7 @@ fn completed_imports_do_not_read_source_files() {
source_content_sha256: format!("{:x}", Sha256::digest(contents)), source_content_sha256: format!("{:x}", Sha256::digest(contents)),
imported_thread_id, imported_thread_id,
connector_names: Vec::new(), connector_names: Vec::new(),
title: Some("Imported session".to_string()),
}], }],
) )
.expect("record completed imports"); .expect("record completed imports");
@@ -47,6 +48,7 @@ fn completed_imports_do_not_read_source_files() {
assert_eq!(ledger.records[0].source_path, source_path); assert_eq!(ledger.records[0].source_path, source_path);
assert_eq!(ledger.records[0].imported_thread_id, imported_thread_id); assert_eq!(ledger.records[0].imported_thread_id, imported_thread_id);
assert_eq!(ledger.records[0].source_modified_at, None); assert_eq!(ledger.records[0].source_modified_at, None);
assert_eq!(ledger.records[0].title.as_deref(), Some("Imported session"));
} }
#[test] #[test]
@@ -68,6 +70,7 @@ fn completed_import_refreshes_existing_record_metadata() {
source_content_sha256: content_sha256.clone(), source_content_sha256: content_sha256.clone(),
imported_thread_id: first_thread_id, imported_thread_id: first_thread_id,
connector_names: vec!["Gmail".to_string()], connector_names: vec!["Gmail".to_string()],
title: Some("First title".to_string()),
}], }],
) )
.expect("record first import"); .expect("record first import");
@@ -78,6 +81,7 @@ fn completed_import_refreshes_existing_record_metadata() {
source_content_sha256: content_sha256, source_content_sha256: content_sha256,
imported_thread_id: second_thread_id, imported_thread_id: second_thread_id,
connector_names: vec!["Slack".to_string()], connector_names: vec!["Slack".to_string()],
title: Some("Second title".to_string()),
}], }],
) )
.expect("record replacement import"); .expect("record replacement import");
@@ -88,6 +92,7 @@ fn completed_import_refreshes_existing_record_metadata() {
assert_eq!(ledger.records[0].imported_thread_id, second_thread_id); assert_eq!(ledger.records[0].imported_thread_id, second_thread_id);
assert!(ledger.records[0].source_modified_at.is_some()); assert!(ledger.records[0].source_modified_at.is_some());
assert_eq!(ledger.records[0].connector_names, vec!["Slack"]); assert_eq!(ledger.records[0].connector_names, vec!["Slack"]);
assert_eq!(ledger.records[0].title.as_deref(), Some("Second title"));
} }
#[test] #[test]
@@ -105,18 +110,21 @@ fn connector_candidates_use_latest_import_for_each_source() {
source_content_sha256: "first-version".to_string(), source_content_sha256: "first-version".to_string(),
imported_thread_id: ThreadId::new(), imported_thread_id: ThreadId::new(),
connector_names: vec!["Gmail".to_string()], connector_names: vec!["Gmail".to_string()],
title: None,
}, },
CompletedExternalAgentSessionImport { CompletedExternalAgentSessionImport {
source_path: first_source, source_path: first_source,
source_content_sha256: "second-version".to_string(), source_content_sha256: "second-version".to_string(),
imported_thread_id: ThreadId::new(), imported_thread_id: ThreadId::new(),
connector_names: vec!["Slack".to_string()], connector_names: vec!["Slack".to_string()],
title: None,
}, },
CompletedExternalAgentSessionImport { CompletedExternalAgentSessionImport {
source_path: second_source, source_path: second_source,
source_content_sha256: "only-version".to_string(), source_content_sha256: "only-version".to_string(),
imported_thread_id: ThreadId::new(), imported_thread_id: ThreadId::new(),
connector_names: vec!["Gmail".to_string(), "Slack".to_string()], connector_names: vec!["Gmail".to_string(), "Slack".to_string()],
title: None,
}, },
], ],
) )
@@ -12,6 +12,8 @@ pub struct ExternalAgentConfigImportSuccessRecord {
pub cwd: Option<PathBuf>, pub cwd: Option<PathBuf>,
pub source: Option<String>, pub source: Option<String>,
pub target: Option<String>, pub target: Option<String>,
#[serde(default)]
pub title: Option<String>,
} }
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
@@ -20,6 +20,7 @@ async fn records_completion_by_import_id() -> anyhow::Result<()> {
cwd: None, cwd: None,
source: Some("settings.json".to_string()), source: Some("settings.json".to_string()),
target: Some("config.toml".to_string()), target: Some("config.toml".to_string()),
title: None,
}], }],
&[], &[],
) )
@@ -34,12 +35,14 @@ async fn records_completion_by_import_id() -> anyhow::Result<()> {
cwd: None, cwd: None,
source: Some("settings.json".to_string()), source: Some("settings.json".to_string()),
target: Some("config.toml".to_string()), target: Some("config.toml".to_string()),
title: None,
}, },
ExternalAgentConfigImportSuccessRecord { ExternalAgentConfigImportSuccessRecord {
item_type: "MCP_SERVER_CONFIG".to_string(), item_type: "MCP_SERVER_CONFIG".to_string(),
cwd: None, cwd: None,
source: Some("github".to_string()), source: Some("github".to_string()),
target: Some("github".to_string()), target: Some("github".to_string()),
title: None,
}, },
], ],
&[ExternalAgentConfigImportFailureRecord { &[ExternalAgentConfigImportFailureRecord {
@@ -65,12 +68,14 @@ async fn records_completion_by_import_id() -> anyhow::Result<()> {
cwd: None, cwd: None,
source: Some("settings.json".to_string()), source: Some("settings.json".to_string()),
target: Some("config.toml".to_string()), target: Some("config.toml".to_string()),
title: None,
}, },
ExternalAgentConfigImportSuccessRecord { ExternalAgentConfigImportSuccessRecord {
item_type: "MCP_SERVER_CONFIG".to_string(), item_type: "MCP_SERVER_CONFIG".to_string(),
cwd: None, cwd: None,
source: Some("github".to_string()), source: Some("github".to_string()),
target: Some("github".to_string()), target: Some("github".to_string()),
title: None,
} }
], ],
failures: vec![ExternalAgentConfigImportFailureRecord { failures: vec![ExternalAgentConfigImportFailureRecord {
@@ -106,12 +111,14 @@ async fn records_completion_by_import_id() -> anyhow::Result<()> {
cwd: None, cwd: None,
source: Some("settings.json".to_string()), source: Some("settings.json".to_string()),
target: Some("config.toml".to_string()), target: Some("config.toml".to_string()),
title: None,
}, },
ExternalAgentConfigImportSuccessRecord { ExternalAgentConfigImportSuccessRecord {
item_type: "MCP_SERVER_CONFIG".to_string(), item_type: "MCP_SERVER_CONFIG".to_string(),
cwd: None, cwd: None,
source: Some("github".to_string()), source: Some("github".to_string()),
target: Some("github".to_string()), target: Some("github".to_string()),
title: None,
} }
], ],
vec![ExternalAgentConfigImportFailureRecord { vec![ExternalAgentConfigImportFailureRecord {
@@ -127,6 +127,7 @@ fn completed_notification() -> ExternalAgentConfigImportCompletedNotification {
cwd: None, cwd: None,
source: Some("settings.json".to_string()), source: Some("settings.json".to_string()),
target: Some("config.toml".to_string()), target: Some("config.toml".to_string()),
title: None,
}], }],
failures: Vec::new(), failures: Vec::new(),
}, },
@@ -138,12 +139,14 @@ fn completed_notification() -> ExternalAgentConfigImportCompletedNotification {
cwd: None, cwd: None,
source: Some("project-a".to_string()), source: Some("project-a".to_string()),
target: Some("memory resources".to_string()), target: Some("memory resources".to_string()),
title: None,
}, },
ExternalAgentConfigImportItemTypeSuccess { ExternalAgentConfigImportItemTypeSuccess {
item_type: ExternalAgentConfigMigrationItemType::Memory, item_type: ExternalAgentConfigMigrationItemType::Memory,
cwd: None, cwd: None,
source: Some("project-b".to_string()), source: Some("project-b".to_string()),
target: Some("memory resources".to_string()), target: Some("memory resources".to_string()),
title: None,
}, },
], ],
failures: Vec::new(), failures: Vec::new(),
@@ -155,6 +158,7 @@ fn completed_notification() -> ExternalAgentConfigImportCompletedNotification {
cwd: None, cwd: None,
source: Some("formatter@example".to_string()), source: Some("formatter@example".to_string()),
target: Some("formatter@example".to_string()), target: Some("formatter@example".to_string()),
title: None,
}], }],
failures: vec![ExternalAgentConfigImportItemTypeFailure { failures: vec![ExternalAgentConfigImportItemTypeFailure {
item_type: ExternalAgentConfigMigrationItemType::Plugins, item_type: ExternalAgentConfigMigrationItemType::Plugins,