|
|
|
@@ -94,11 +94,11 @@ async function createAgentSession(agent: IAgent, config?: IAgentCreateSessionCon
|
|
|
|
|
return { session, ...chat, chat };
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function discoveredChat(session: URI, external = true): IAgentDiscoveredChat {
|
|
|
|
|
function discoveredChat(session: URI, external = true, modifiedTime = Date.now()): IAgentDiscoveredChat {
|
|
|
|
|
return {
|
|
|
|
|
chat: URI.parse(buildDefaultChatUri(session)),
|
|
|
|
|
startTime: 1,
|
|
|
|
|
modifiedTime: 1,
|
|
|
|
|
startTime: modifiedTime,
|
|
|
|
|
modifiedTime,
|
|
|
|
|
external,
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
@@ -1059,7 +1059,7 @@ suite('AgentService (node dispatcher)', () => {
|
|
|
|
|
// Reopen: a fresh service on the same DB rediscovers the provider-native
|
|
|
|
|
// session and must restore the persisted decision into `_meta`.
|
|
|
|
|
const reopened = disposables.add(new AgentService(new NullLogService(), fileService, createSessionDataService(db), { _serviceBrand: undefined } as IProductService, createNoopGitService()));
|
|
|
|
|
reopened.configurationService.updateRootConfig({ [AgentHostShowExternalSessionsConfigKey]: AgentHostExternalSessionsMode.All });
|
|
|
|
|
reopened.configurationService.updateRootConfig({ [AgentHostShowExternalSessionsConfigKey]: AgentHostExternalSessionsMode.Last30Days });
|
|
|
|
|
const reopenedAgent = new MockAgent('copilot');
|
|
|
|
|
disposables.add(toDisposable(() => reopenedAgent.dispose()));
|
|
|
|
|
(reopenedAgent as unknown as { _sessions: Map<string, URI> })._sessions.set(AgentSession.id(session), session);
|
|
|
|
@@ -1117,7 +1117,7 @@ suite('AgentService (node dispatcher)', () => {
|
|
|
|
|
await timeout(0);
|
|
|
|
|
|
|
|
|
|
const reopened = disposables.add(new AgentService(new NullLogService(), fileService, createSessionDataService(db), { _serviceBrand: undefined } as IProductService, createNoopGitService()));
|
|
|
|
|
reopened.configurationService.updateRootConfig({ [AgentHostShowExternalSessionsConfigKey]: AgentHostExternalSessionsMode.All });
|
|
|
|
|
reopened.configurationService.updateRootConfig({ [AgentHostShowExternalSessionsConfigKey]: AgentHostExternalSessionsMode.Last30Days });
|
|
|
|
|
const reopenedAgent = new MockAgent('copilot');
|
|
|
|
|
disposables.add(toDisposable(() => reopenedAgent.dispose()));
|
|
|
|
|
(reopenedAgent as unknown as { _sessions: Map<string, URI> })._sessions.set(AgentSession.id(session), session);
|
|
|
|
@@ -2934,11 +2934,11 @@ suite('AgentService (node dispatcher)', () => {
|
|
|
|
|
suite('aggregation', () => {
|
|
|
|
|
|
|
|
|
|
class TimedExternalAgent extends MockAgent {
|
|
|
|
|
readonly catalog = new Map<string, { session: URI; modifiedTime: number }>();
|
|
|
|
|
readonly catalog = new Map<string, { session: URI; modifiedTime: number; _meta?: IAgentSessionMetadata['_meta'] }>();
|
|
|
|
|
|
|
|
|
|
addSession(id: string, modifiedTime: number): URI {
|
|
|
|
|
addSession(id: string, modifiedTime: number, _meta?: IAgentSessionMetadata['_meta']): URI {
|
|
|
|
|
const session = AgentSession.uri(this.id, id);
|
|
|
|
|
this.catalog.set(id, { session, modifiedTime });
|
|
|
|
|
this.catalog.set(id, { session, modifiedTime, _meta });
|
|
|
|
|
(this as unknown as { _sessions: Map<string, URI> })._sessions.set(id, session);
|
|
|
|
|
return session;
|
|
|
|
|
}
|
|
|
|
@@ -2948,13 +2948,14 @@ suite('AgentService (node dispatcher)', () => {
|
|
|
|
|
chat: URI.parse(buildDefaultChatUri(entry.session)),
|
|
|
|
|
startTime: entry.modifiedTime,
|
|
|
|
|
modifiedTime: entry.modifiedTime,
|
|
|
|
|
...(entry._meta ? { _meta: entry._meta } : {}),
|
|
|
|
|
}));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
override async getChatMetadata(chat: URI, context: URI | IAgentChatContext): Promise<IAgentChatMetadata | undefined> {
|
|
|
|
|
const session = resolveAgentChatContext(context, chat).configurationResource;
|
|
|
|
|
const entry = this.catalog.get(AgentSession.id(session));
|
|
|
|
|
return entry ? { chat, startTime: entry.modifiedTime, modifiedTime: entry.modifiedTime } : undefined;
|
|
|
|
|
return entry ? { chat, startTime: entry.modifiedTime, modifiedTime: entry.modifiedTime, ...(entry._meta ? { _meta: entry._meta } : {}) } : undefined;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@@ -3024,7 +3025,7 @@ suite('AgentService (node dispatcher)', () => {
|
|
|
|
|
test('listSessions discovers provider-native sessions as external and restore preserves provenance', async () => {
|
|
|
|
|
const db = new TestSessionDatabase();
|
|
|
|
|
const svc = disposables.add(new AgentService(new NullLogService(), fileService, createSessionDataService(db), { _serviceBrand: undefined } as IProductService, createNoopGitService()));
|
|
|
|
|
svc.configurationService.updateRootConfig({ [AgentHostShowExternalSessionsConfigKey]: AgentHostExternalSessionsMode.All });
|
|
|
|
|
svc.configurationService.updateRootConfig({ [AgentHostShowExternalSessionsConfigKey]: AgentHostExternalSessionsMode.Last30Days });
|
|
|
|
|
const agent = new MockAgent('copilot');
|
|
|
|
|
disposables.add(toDisposable(() => agent.dispose()));
|
|
|
|
|
|
|
|
|
@@ -3062,6 +3063,54 @@ suite('AgentService (node dispatcher)', () => {
|
|
|
|
|
assert.strictEqual(await db.getMetadata(AH_META_IS_READ_DB_KEY), '');
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
test('discovery does not ingest external sessions older than 30 days', async () => {
|
|
|
|
|
const day = 24 * 60 * 60 * 1000;
|
|
|
|
|
const now = Date.now();
|
|
|
|
|
const svc = createExternalSessionService(() => now);
|
|
|
|
|
const agent = disposables.add(new TimedExternalAgent('copilot'));
|
|
|
|
|
const stale = agent.addSession('stale', now - 30 * day - 1);
|
|
|
|
|
const fresh = agent.addSession('fresh', now - 30 * day + 60_000);
|
|
|
|
|
setExternalSessionsMode(svc, AgentHostExternalSessionsMode.Last30Days, 1);
|
|
|
|
|
await waitForSessionListReconciliation(svc);
|
|
|
|
|
svc.registerProvider(agent);
|
|
|
|
|
await (svc as unknown as { _registerDiscoveredChats(provider: IAgent, chats: readonly IAgentDiscoveredChat[]): Promise<boolean> })._registerDiscoveredChats(agent, [
|
|
|
|
|
{ chat: URI.parse(buildDefaultChatUri(stale)), startTime: now - 30 * day - 1, modifiedTime: now - 30 * day - 1, external: true },
|
|
|
|
|
{ chat: URI.parse(buildDefaultChatUri(fresh)), startTime: now - 30 * day + 60_000, modifiedTime: now - 30 * day + 60_000, external: true },
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
const listed = (await svc.listSessions()).map(session => AgentSession.id(session.session)).sort();
|
|
|
|
|
const registered = new Set((await svc.getRegisteredSessions()).map(session => session.toString()));
|
|
|
|
|
|
|
|
|
|
assert.deepStrictEqual({
|
|
|
|
|
listed,
|
|
|
|
|
registered: [...registered].sort(),
|
|
|
|
|
}, {
|
|
|
|
|
listed: [AgentSession.id(fresh)],
|
|
|
|
|
registered: [fresh.toString()],
|
|
|
|
|
});
|
|
|
|
|
assert.ok(!registered.has(stale.toString()));
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
test('prune removes stale external sessions but keeps adoptable-legacy sessions', async () => {
|
|
|
|
|
const day = 24 * 60 * 60 * 1000;
|
|
|
|
|
const now = Date.now();
|
|
|
|
|
const svc = createExternalSessionService(() => now);
|
|
|
|
|
const agent = disposables.add(new TimedExternalAgent('copilot'));
|
|
|
|
|
const stale = agent.addSession('stale-prune', now - 30 * day - 1);
|
|
|
|
|
const staleAdoptable = agent.addSession('stale-adoptable', now - 30 * day - 1, withSessionEhcliAdoptable(undefined));
|
|
|
|
|
const fresh = agent.addSession('fresh-prune', now - 30 * day);
|
|
|
|
|
svc.registerProvider(agent);
|
|
|
|
|
const sessionRegistry = (svc as unknown as { _sessionRegistry: AgentSessionRegistry })._sessionRegistry;
|
|
|
|
|
await sessionRegistry.register(stale, { provider: 'copilot', startTime: now - 30 * day - 1, source: 'discovery' }, { checkTombstone: true });
|
|
|
|
|
await sessionRegistry.register(staleAdoptable, { provider: 'copilot', startTime: now - 30 * day - 1, source: 'discovery' }, { checkTombstone: true });
|
|
|
|
|
await sessionRegistry.register(fresh, { provider: 'copilot', startTime: now - 30 * day, source: 'discovery' }, { checkTombstone: true });
|
|
|
|
|
|
|
|
|
|
await (svc as unknown as { _pruneStaleExternalSessions(): Promise<void> })._pruneStaleExternalSessions();
|
|
|
|
|
const registered = (await sessionRegistry.list()).map(entry => entry.session.toString()).sort();
|
|
|
|
|
|
|
|
|
|
assert.deepStrictEqual(registered, [fresh.toString(), staleAdoptable.toString()].sort());
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
test('filters external sessions in every mode with inclusive time boundaries', async () => {
|
|
|
|
|
const day = 24 * 60 * 60 * 1000;
|
|
|
|
|
const now = Date.now();
|
|
|
|
@@ -3072,18 +3121,20 @@ suite('AgentService (node dispatcher)', () => {
|
|
|
|
|
agent.addSession('older-than-24-hours', now - day - 1);
|
|
|
|
|
agent.addSession('at-7-days', now - 7 * day);
|
|
|
|
|
agent.addSession('older-than-7-days', now - 7 * day - 1);
|
|
|
|
|
agent.addSession('at-30-days', now - 30 * day);
|
|
|
|
|
agent.addSession('older-than-30-days', now - 30 * day - 1);
|
|
|
|
|
svc.registerProvider(agent);
|
|
|
|
|
|
|
|
|
|
const listedByMode: Record<AgentHostExternalSessionsMode, string[]> = {
|
|
|
|
|
[AgentHostExternalSessionsMode.Recent]: [],
|
|
|
|
|
[AgentHostExternalSessionsMode.None]: [],
|
|
|
|
|
[AgentHostExternalSessionsMode.All]: [],
|
|
|
|
|
[AgentHostExternalSessionsMode.Last30Days]: [],
|
|
|
|
|
[AgentHostExternalSessionsMode.Last24Hours]: [],
|
|
|
|
|
[AgentHostExternalSessionsMode.Last7Days]: [],
|
|
|
|
|
};
|
|
|
|
|
const listedByDefault = (await svc.listSessions()).map(session => AgentSession.id(session.session)).sort();
|
|
|
|
|
let clientSeq = 1;
|
|
|
|
|
for (const mode of [AgentHostExternalSessionsMode.Recent, AgentHostExternalSessionsMode.None, AgentHostExternalSessionsMode.All, AgentHostExternalSessionsMode.Last24Hours, AgentHostExternalSessionsMode.Last7Days]) {
|
|
|
|
|
for (const mode of [AgentHostExternalSessionsMode.Recent, AgentHostExternalSessionsMode.None, AgentHostExternalSessionsMode.Last30Days, AgentHostExternalSessionsMode.Last24Hours, AgentHostExternalSessionsMode.Last7Days]) {
|
|
|
|
|
setExternalSessionsMode(svc, mode, clientSeq++);
|
|
|
|
|
await waitForSessionListReconciliation(svc);
|
|
|
|
|
listedByMode[mode] = (await svc.listSessions()).map(session => AgentSession.id(session.session)).sort();
|
|
|
|
@@ -3094,7 +3145,7 @@ suite('AgentService (node dispatcher)', () => {
|
|
|
|
|
listedByMode: {
|
|
|
|
|
[AgentHostExternalSessionsMode.Recent]: ['at-24-hours', 'recent'],
|
|
|
|
|
[AgentHostExternalSessionsMode.None]: [],
|
|
|
|
|
[AgentHostExternalSessionsMode.All]: ['at-24-hours', 'at-7-days', 'older-than-24-hours', 'older-than-7-days', 'recent'],
|
|
|
|
|
[AgentHostExternalSessionsMode.Last30Days]: ['at-24-hours', 'at-30-days', 'at-7-days', 'older-than-24-hours', 'older-than-7-days', 'recent'],
|
|
|
|
|
[AgentHostExternalSessionsMode.Last24Hours]: ['at-24-hours', 'recent'],
|
|
|
|
|
[AgentHostExternalSessionsMode.Last7Days]: ['at-24-hours', 'at-7-days', 'older-than-24-hours', 'recent'],
|
|
|
|
|
},
|
|
|
|
@@ -3109,7 +3160,7 @@ suite('AgentService (node dispatcher)', () => {
|
|
|
|
|
agent.addSession('external-one', now);
|
|
|
|
|
agent.addSession('external-two', now);
|
|
|
|
|
svc.registerProvider(agent);
|
|
|
|
|
await svc.listSessions(AgentHostExternalSessionsMode.All);
|
|
|
|
|
await svc.listSessions(AgentHostExternalSessionsMode.Last30Days);
|
|
|
|
|
|
|
|
|
|
// A catalog pass otherwise opens every registered session's database,
|
|
|
|
|
// so a mode that discards the row regardless must not pay for it.
|
|
|
|
@@ -3124,7 +3175,7 @@ suite('AgentService (node dispatcher)', () => {
|
|
|
|
|
const hidden = (await svc.listSessions(AgentHostExternalSessionsMode.None)).map(session => AgentSession.id(session.session));
|
|
|
|
|
const openedWhileHidden = [...new Set(opened)].sort();
|
|
|
|
|
opened.length = 0;
|
|
|
|
|
const visible = (await svc.listSessions(AgentHostExternalSessionsMode.All)).map(session => AgentSession.id(session.session)).sort();
|
|
|
|
|
const visible = (await svc.listSessions(AgentHostExternalSessionsMode.Last30Days)).map(session => AgentSession.id(session.session)).sort();
|
|
|
|
|
|
|
|
|
|
assert.deepStrictEqual({ hidden, openedWhileHidden, visible, openedWhileVisible: [...new Set(opened)].sort() }, {
|
|
|
|
|
hidden: [],
|
|
|
|
@@ -3146,7 +3197,7 @@ suite('AgentService (node dispatcher)', () => {
|
|
|
|
|
agent.addSession('yesterday', now - day);
|
|
|
|
|
agent.addSession('last-week', now - 6 * day);
|
|
|
|
|
svc.registerProvider(agent);
|
|
|
|
|
setExternalSessionsMode(svc, AgentHostExternalSessionsMode.All, 1);
|
|
|
|
|
setExternalSessionsMode(svc, AgentHostExternalSessionsMode.Last30Days, 1);
|
|
|
|
|
await waitForSessionListReconciliation(svc);
|
|
|
|
|
|
|
|
|
|
// Each `listSessions` is one walk over every registered session's
|
|
|
|
@@ -3172,7 +3223,7 @@ suite('AgentService (node dispatcher)', () => {
|
|
|
|
|
transitionModes,
|
|
|
|
|
visible: (await svc.listSessions()).map(session => AgentSession.id(session.session)).sort(),
|
|
|
|
|
}, {
|
|
|
|
|
transitionModes: [AgentHostExternalSessionsMode.All],
|
|
|
|
|
transitionModes: [AgentHostExternalSessionsMode.Last30Days],
|
|
|
|
|
visible: ['recent', 'yesterday'],
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
@@ -3268,7 +3319,7 @@ suite('AgentService (node dispatcher)', () => {
|
|
|
|
|
test('external discovery reconciles against a mode change that completes while registration is in flight', async () => {
|
|
|
|
|
const now = Date.now();
|
|
|
|
|
const svc = createExternalSessionService(() => now);
|
|
|
|
|
setExternalSessionsMode(svc, AgentHostExternalSessionsMode.All, 1);
|
|
|
|
|
setExternalSessionsMode(svc, AgentHostExternalSessionsMode.Last30Days, 1);
|
|
|
|
|
await waitForSessionListReconciliation(svc);
|
|
|
|
|
const agent = disposables.add(new TimedExternalAgent('copilot'));
|
|
|
|
|
svc.registerProvider(agent);
|
|
|
|
@@ -3380,7 +3431,7 @@ suite('AgentService (node dispatcher)', () => {
|
|
|
|
|
await waitForSessionListReconciliation(svc);
|
|
|
|
|
notifications.length = 0;
|
|
|
|
|
|
|
|
|
|
setExternalSessionsMode(svc, AgentHostExternalSessionsMode.All, 2);
|
|
|
|
|
setExternalSessionsMode(svc, AgentHostExternalSessionsMode.Last30Days, 2);
|
|
|
|
|
await waitForSessionListReconciliation(svc);
|
|
|
|
|
setExternalSessionsMode(svc, AgentHostExternalSessionsMode.None, 3);
|
|
|
|
|
await waitForSessionListReconciliation(svc);
|
|
|
|
@@ -3391,7 +3442,7 @@ suite('AgentService (node dispatcher)', () => {
|
|
|
|
|
test('unpublishes and republishes a restored external session as the configured mode changes', async () => {
|
|
|
|
|
const now = Date.now();
|
|
|
|
|
const svc = createExternalSessionService(() => now);
|
|
|
|
|
setExternalSessionsMode(svc, AgentHostExternalSessionsMode.All, 1);
|
|
|
|
|
setExternalSessionsMode(svc, AgentHostExternalSessionsMode.Last30Days, 1);
|
|
|
|
|
await waitForSessionListReconciliation(svc);
|
|
|
|
|
const agent = disposables.add(new TimedExternalAgent('copilot'));
|
|
|
|
|
const session = agent.addSession('restored-external', now);
|
|
|
|
@@ -3411,7 +3462,7 @@ suite('AgentService (node dispatcher)', () => {
|
|
|
|
|
setExternalSessionsMode(svc, AgentHostExternalSessionsMode.None, 2);
|
|
|
|
|
await waitForSessionListReconciliation(svc);
|
|
|
|
|
const hidden = (await svc.listSessions()).map(entry => entry.session.toString());
|
|
|
|
|
setExternalSessionsMode(svc, AgentHostExternalSessionsMode.All, 3);
|
|
|
|
|
setExternalSessionsMode(svc, AgentHostExternalSessionsMode.Last30Days, 3);
|
|
|
|
|
await waitForSessionListReconciliation(svc);
|
|
|
|
|
|
|
|
|
|
assert.deepStrictEqual({
|
|
|
|
@@ -3454,7 +3505,7 @@ suite('AgentService (node dispatcher)', () => {
|
|
|
|
|
await svc.restoreSession(session);
|
|
|
|
|
notifications.length = 0;
|
|
|
|
|
|
|
|
|
|
setExternalSessionsMode(svc, AgentHostExternalSessionsMode.All, 2);
|
|
|
|
|
setExternalSessionsMode(svc, AgentHostExternalSessionsMode.Last30Days, 2);
|
|
|
|
|
await waitForSessionListReconciliation(svc);
|
|
|
|
|
|
|
|
|
|
assert.deepStrictEqual({
|
|
|
|
@@ -3714,12 +3765,12 @@ suite('AgentService (node dispatcher)', () => {
|
|
|
|
|
|
|
|
|
|
override async listExternalChats(): Promise<IAgentChatMetadata[]> {
|
|
|
|
|
this.externalCalls++;
|
|
|
|
|
return [{ chat: URI.parse(buildDefaultChatUri(external)), startTime: 1, modifiedTime: 1 }];
|
|
|
|
|
return [{ chat: URI.parse(buildDefaultChatUri(external)), startTime: Date.now(), modifiedTime: Date.now() }];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
override async listChatsToMigrate(): Promise<IAgentChatMetadata[]> {
|
|
|
|
|
this.legacyCalls++;
|
|
|
|
|
return [{ chat: URI.parse(buildDefaultChatUri(legacy)), startTime: 2, modifiedTime: 2 }];
|
|
|
|
|
return [{ chat: URI.parse(buildDefaultChatUri(legacy)), startTime: Date.now(), modifiedTime: Date.now() }];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
override fireDiscoveredChats(chats: readonly IAgentDiscoveredChat[]): void { this._onDidDiscoverChats.fire(chats); }
|
|
|
|
@@ -3811,8 +3862,8 @@ suite('AgentService (node dispatcher)', () => {
|
|
|
|
|
|
|
|
|
|
await (svc as unknown as { _announceSurfacedSession(meta: IAgentSessionMetadata, provider: string): Promise<void> })._announceSurfacedSession({
|
|
|
|
|
session,
|
|
|
|
|
startTime: 1,
|
|
|
|
|
modifiedTime: 1,
|
|
|
|
|
startTime: Date.now(),
|
|
|
|
|
modifiedTime: Date.now(),
|
|
|
|
|
}, agent.id);
|
|
|
|
|
assert.strictEqual(svc.stateManager.getSurfacedSessionSummary(session.toString())?.resource, session.toString());
|
|
|
|
|
});
|
|
|
|
@@ -3821,8 +3872,8 @@ suite('AgentService (node dispatcher)', () => {
|
|
|
|
|
class MixedMigrationAgent extends MockAgent {
|
|
|
|
|
override async listChatsToMigrate(): Promise<IAgentChatMetadata[]> {
|
|
|
|
|
return [
|
|
|
|
|
{ chat: URI.parse(buildDefaultChatUri(restored)), startTime: 1, modifiedTime: 1 },
|
|
|
|
|
{ chat: URI.parse(buildDefaultChatUri(external)), startTime: 2, modifiedTime: 2 },
|
|
|
|
|
{ chat: URI.parse(buildDefaultChatUri(restored)), startTime: Date.now(), modifiedTime: Date.now() },
|
|
|
|
|
{ chat: URI.parse(buildDefaultChatUri(external)), startTime: Date.now(), modifiedTime: Date.now() },
|
|
|
|
|
];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
@@ -3957,7 +4008,7 @@ suite('AgentService (node dispatcher)', () => {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
const svc = disposables.add(new AgentService(new NullLogService(), fileService, createSessionDataService(), { _serviceBrand: undefined } as IProductService, createNoopGitService()));
|
|
|
|
|
svc.configurationService.updateRootConfig({ [AgentHostShowExternalSessionsConfigKey]: AgentHostExternalSessionsMode.All });
|
|
|
|
|
svc.configurationService.updateRootConfig({ [AgentHostShowExternalSessionsConfigKey]: AgentHostExternalSessionsMode.Last30Days });
|
|
|
|
|
const agent = disposables.add(new GatedListAgent('copilot'));
|
|
|
|
|
svc.registerProvider(agent);
|
|
|
|
|
const legacy = AgentSession.uri('copilot', 'legacy-concurrent');
|
|
|
|
@@ -4007,7 +4058,7 @@ suite('AgentService (node dispatcher)', () => {
|
|
|
|
|
}
|
|
|
|
|
const db = new TestSessionDatabase();
|
|
|
|
|
const svc = disposables.add(new AgentService(new NullLogService(), fileService, createSessionDataService(db), { _serviceBrand: undefined } as IProductService, createNoopGitService()));
|
|
|
|
|
svc.configurationService.updateRootConfig({ [AgentHostShowExternalSessionsConfigKey]: AgentHostExternalSessionsMode.All });
|
|
|
|
|
svc.configurationService.updateRootConfig({ [AgentHostShowExternalSessionsConfigKey]: AgentHostExternalSessionsMode.Last30Days });
|
|
|
|
|
const agent = disposables.add(new TransientListFailureAgent('copilot'));
|
|
|
|
|
svc.registerProvider(agent);
|
|
|
|
|
const legacy = AgentSession.uri('copilot', 'legacy-session');
|
|
|
|
@@ -4028,7 +4079,7 @@ suite('AgentService (node dispatcher)', () => {
|
|
|
|
|
|
|
|
|
|
test('a late-registered provider gets its own native discovery pass', async () => {
|
|
|
|
|
const svc = disposables.add(new AgentService(new NullLogService(), fileService, createSessionDataService(), { _serviceBrand: undefined } as IProductService, createNoopGitService()));
|
|
|
|
|
svc.configurationService.updateRootConfig({ [AgentHostShowExternalSessionsConfigKey]: AgentHostExternalSessionsMode.All });
|
|
|
|
|
svc.configurationService.updateRootConfig({ [AgentHostShowExternalSessionsConfigKey]: AgentHostExternalSessionsMode.Last30Days });
|
|
|
|
|
const early = disposables.add(new MockAgent('copilot'));
|
|
|
|
|
svc.registerProvider(early);
|
|
|
|
|
|
|
|
|
@@ -4166,7 +4217,7 @@ suite('AgentService (node dispatcher)', () => {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
const svc = disposables.add(new AgentService(new NullLogService(), fileService, createSessionDataService(), { _serviceBrand: undefined } as IProductService, createNoopGitService()));
|
|
|
|
|
svc.configurationService.updateRootConfig({ [AgentHostShowExternalSessionsConfigKey]: AgentHostExternalSessionsMode.All });
|
|
|
|
|
svc.configurationService.updateRootConfig({ [AgentHostShowExternalSessionsConfigKey]: AgentHostExternalSessionsMode.Last30Days });
|
|
|
|
|
const providerA = disposables.add(new CountingAgent('copilot'));
|
|
|
|
|
const providerB = disposables.add(new FailingThenRecoveringAgent('other'));
|
|
|
|
|
|
|
|
|
@@ -4217,7 +4268,7 @@ suite('AgentService (node dispatcher)', () => {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
const svc = disposables.add(new AgentService(new NullLogService(), fileService, createSessionDataService(), { _serviceBrand: undefined } as IProductService, createNoopGitService()));
|
|
|
|
|
svc.configurationService.updateRootConfig({ [AgentHostShowExternalSessionsConfigKey]: AgentHostExternalSessionsMode.All });
|
|
|
|
|
svc.configurationService.updateRootConfig({ [AgentHostShowExternalSessionsConfigKey]: AgentHostExternalSessionsMode.Last30Days });
|
|
|
|
|
const agent = disposables.add(new NotYetEnumerableAgent('copilot'));
|
|
|
|
|
const originalListExternalChats = agent.listExternalChats.bind(agent);
|
|
|
|
|
(agent as unknown as { listExternalChats: () => Promise<readonly IAgentChatMetadata[] | undefined> }).listExternalChats = async () => {
|
|
|
|
@@ -4255,7 +4306,7 @@ suite('AgentService (node dispatcher)', () => {
|
|
|
|
|
await db.registerSession(existing.toString(), { provider: 'copilot', startTime: 1, source: 'explicit' }, { checkTombstone: false });
|
|
|
|
|
const writesBeforeUnavailable = db.registryWriteAttempts;
|
|
|
|
|
const svc = disposables.add(new AgentService(new NullLogService(), fileService, createSessionDataService(), { _serviceBrand: undefined } as IProductService, createNoopGitService(), undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, db));
|
|
|
|
|
svc.configurationService.updateRootConfig({ [AgentHostShowExternalSessionsConfigKey]: AgentHostExternalSessionsMode.All });
|
|
|
|
|
svc.configurationService.updateRootConfig({ [AgentHostShowExternalSessionsConfigKey]: AgentHostExternalSessionsMode.Last30Days });
|
|
|
|
|
const agent = disposables.add(new NotYetMigratableAgent('copilot'));
|
|
|
|
|
const legacy = AgentSession.uri('copilot', 'legacy-migration-not-ready');
|
|
|
|
|
(agent as unknown as { _sessions: Map<string, URI> })._sessions.set(AgentSession.id(legacy), legacy);
|
|
|
|
@@ -4263,7 +4314,7 @@ suite('AgentService (node dispatcher)', () => {
|
|
|
|
|
(agent as unknown as { listChatsToMigrate: () => Promise<readonly IAgentChatMetadata[] | undefined> }).listChatsToMigrate = async () => {
|
|
|
|
|
agent.migrationCalls++;
|
|
|
|
|
return agent.enumerable
|
|
|
|
|
? [{ chat: URI.parse(buildDefaultChatUri(legacy)), startTime: 1, modifiedTime: 1 }]
|
|
|
|
|
? [{ chat: URI.parse(buildDefaultChatUri(legacy)), startTime: Date.now(), modifiedTime: Date.now() }]
|
|
|
|
|
: undefined;
|
|
|
|
|
};
|
|
|
|
|
svc.registerProvider(agent);
|
|
|
|
@@ -4321,14 +4372,14 @@ suite('AgentService (node dispatcher)', () => {
|
|
|
|
|
await timeout(0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const all = svc.listSessions(AgentHostExternalSessionsMode.All);
|
|
|
|
|
const last30Days = svc.listSessions(AgentHostExternalSessionsMode.Last30Days);
|
|
|
|
|
const recent = svc.listSessions(AgentHostExternalSessionsMode.Recent);
|
|
|
|
|
for (let i = 0; i < 20 && agent.catalogCalls < 2; i++) {
|
|
|
|
|
await timeout(0);
|
|
|
|
|
}
|
|
|
|
|
assert.strictEqual(agent.catalogCalls, 2, 'overlapping computations must share the replacement retry');
|
|
|
|
|
retryGate.complete();
|
|
|
|
|
await Promise.all([all, recent]);
|
|
|
|
|
await Promise.all([last30Days, recent]);
|
|
|
|
|
assert.strictEqual(agent.catalogCalls, 2, 'a losing caller must await the installed retry instead of queueing another');
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
@@ -4345,7 +4396,8 @@ suite('AgentService (node dispatcher)', () => {
|
|
|
|
|
}
|
|
|
|
|
const db = new TransientRegistryWriteDatabase();
|
|
|
|
|
const svc = disposables.add(new AgentService(new NullLogService(), fileService, createSessionDataService(), { _serviceBrand: undefined } as IProductService, createNoopGitService(), undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, db));
|
|
|
|
|
svc.configurationService.updateRootConfig({ [AgentHostShowExternalSessionsConfigKey]: AgentHostExternalSessionsMode.All });
|
|
|
|
|
svc.configurationService.updateRootConfig({ [AgentHostShowExternalSessionsConfigKey]: AgentHostExternalSessionsMode.Last30Days });
|
|
|
|
|
svc.configurationService.updateRootConfig({ [AgentHostMigrateLegacyCopilotCliEnabledConfigKey]: true });
|
|
|
|
|
const copilot = disposables.add(new CatalogAgent('copilot'));
|
|
|
|
|
const claude = disposables.add(new CatalogAgent('claude'));
|
|
|
|
|
const copilotSession = AgentSession.uri('copilot', 'complete-provider');
|
|
|
|
@@ -4598,7 +4650,7 @@ suite('AgentService (node dispatcher)', () => {
|
|
|
|
|
// Simulate an old database whose legacy one-time marker is set.
|
|
|
|
|
await db.markSessionRegistryBackfilled();
|
|
|
|
|
const svc = disposables.add(new AgentService(new NullLogService(), fileService, createSessionDataService(), { _serviceBrand: undefined } as IProductService, createNoopGitService(), undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, db));
|
|
|
|
|
svc.configurationService.updateRootConfig({ [AgentHostShowExternalSessionsConfigKey]: AgentHostExternalSessionsMode.All });
|
|
|
|
|
svc.configurationService.updateRootConfig({ [AgentHostShowExternalSessionsConfigKey]: AgentHostExternalSessionsMode.Last30Days });
|
|
|
|
|
const agent = disposables.add(new CountingAgent('copilot'));
|
|
|
|
|
const legacy = AgentSession.uri('copilot', 'old-db-native-session');
|
|
|
|
|
(agent as unknown as { _sessions: Map<string, URI> })._sessions.set(AgentSession.id(legacy), legacy);
|
|
|
|
@@ -4812,7 +4864,7 @@ suite('AgentService (node dispatcher)', () => {
|
|
|
|
|
(agent as unknown as { _sessions: Map<string, URI> })._sessions.set(sessionId, sessionUri);
|
|
|
|
|
|
|
|
|
|
const svc = disposables.add(new AgentService(new NullLogService(), fileService, sessionDataService, { _serviceBrand: undefined } as IProductService, createNoopGitService()));
|
|
|
|
|
svc.configurationService.updateRootConfig({ [AgentHostShowExternalSessionsConfigKey]: AgentHostExternalSessionsMode.All });
|
|
|
|
|
svc.configurationService.updateRootConfig({ [AgentHostShowExternalSessionsConfigKey]: AgentHostExternalSessionsMode.Last30Days });
|
|
|
|
|
svc.registerProvider(agent);
|
|
|
|
|
|
|
|
|
|
const sessions = await svc.listSessions();
|
|
|
|
@@ -4858,7 +4910,7 @@ suite('AgentService (node dispatcher)', () => {
|
|
|
|
|
(agent as unknown as { _sessions: Map<string, URI> })._sessions.set(sessionId, sessionUri);
|
|
|
|
|
|
|
|
|
|
const svc = disposables.add(new AgentService(new NullLogService(), fileService, sessionDataService, { _serviceBrand: undefined } as IProductService, createNoopGitService()));
|
|
|
|
|
svc.configurationService.updateRootConfig({ [AgentHostShowExternalSessionsConfigKey]: AgentHostExternalSessionsMode.All });
|
|
|
|
|
svc.configurationService.updateRootConfig({ [AgentHostShowExternalSessionsConfigKey]: AgentHostExternalSessionsMode.Last30Days });
|
|
|
|
|
svc.registerProvider(agent);
|
|
|
|
|
|
|
|
|
|
const sessions = await svc.listSessions();
|
|
|
|
@@ -4881,7 +4933,7 @@ suite('AgentService (node dispatcher)', () => {
|
|
|
|
|
};
|
|
|
|
|
(agent as unknown as { _sessions: Map<string, URI> })._sessions.set(sessionId, sessionUri);
|
|
|
|
|
const svc = disposables.add(new AgentService(new NullLogService(), fileService, createSessionDataService(db), { _serviceBrand: undefined } as IProductService, createNoopGitService()));
|
|
|
|
|
svc.configurationService.updateRootConfig({ [AgentHostShowExternalSessionsConfigKey]: AgentHostExternalSessionsMode.All });
|
|
|
|
|
svc.configurationService.updateRootConfig({ [AgentHostShowExternalSessionsConfigKey]: AgentHostExternalSessionsMode.Last30Days });
|
|
|
|
|
svc.registerProvider(agent);
|
|
|
|
|
|
|
|
|
|
const sessions = await svc.listSessions();
|
|
|
|
@@ -4902,7 +4954,7 @@ suite('AgentService (node dispatcher)', () => {
|
|
|
|
|
disposables.add(toDisposable(() => agent.dispose()));
|
|
|
|
|
(agent as unknown as { _sessions: Map<string, URI> })._sessions.set(sessionId, sessionUri);
|
|
|
|
|
const svc = disposables.add(new AgentService(new NullLogService(), fileService, createSessionDataService(db), { _serviceBrand: undefined } as IProductService, createNoopGitService()));
|
|
|
|
|
svc.configurationService.updateRootConfig({ [AgentHostShowExternalSessionsConfigKey]: AgentHostExternalSessionsMode.All });
|
|
|
|
|
svc.configurationService.updateRootConfig({ [AgentHostShowExternalSessionsConfigKey]: AgentHostExternalSessionsMode.Last30Days });
|
|
|
|
|
svc.registerProvider(agent);
|
|
|
|
|
|
|
|
|
|
const sessions = await svc.listSessions();
|
|
|
|
@@ -4920,7 +4972,7 @@ suite('AgentService (node dispatcher)', () => {
|
|
|
|
|
};
|
|
|
|
|
(agent as unknown as { _sessions: Map<string, URI> })._sessions.set(sessionId, sessionUri);
|
|
|
|
|
const svc = disposables.add(new AgentService(new NullLogService(), fileService, nullSessionDataService, { _serviceBrand: undefined } as IProductService, createNoopGitService()));
|
|
|
|
|
svc.configurationService.updateRootConfig({ [AgentHostShowExternalSessionsConfigKey]: AgentHostExternalSessionsMode.All });
|
|
|
|
|
svc.configurationService.updateRootConfig({ [AgentHostShowExternalSessionsConfigKey]: AgentHostExternalSessionsMode.Last30Days });
|
|
|
|
|
svc.registerProvider(agent);
|
|
|
|
|
|
|
|
|
|
const sessions = await svc.listSessions();
|
|
|
|
@@ -4946,7 +4998,7 @@ suite('AgentService (node dispatcher)', () => {
|
|
|
|
|
return [];
|
|
|
|
|
};
|
|
|
|
|
const svc = disposables.add(new AgentService(new NullLogService(), fileService, createSessionDataService(db), { _serviceBrand: undefined } as IProductService, gitService));
|
|
|
|
|
svc.configurationService.updateRootConfig({ [AgentHostShowExternalSessionsConfigKey]: AgentHostExternalSessionsMode.All });
|
|
|
|
|
svc.configurationService.updateRootConfig({ [AgentHostShowExternalSessionsConfigKey]: AgentHostExternalSessionsMode.Last30Days });
|
|
|
|
|
svc.registerProvider(agent);
|
|
|
|
|
|
|
|
|
|
const sessions = await svc.listSessions();
|
|
|
|
@@ -4981,7 +5033,7 @@ suite('AgentService (node dispatcher)', () => {
|
|
|
|
|
gitService.getWorktreeRoots = async () => [primaryRoot, linkedCheckout, sessionWorktree];
|
|
|
|
|
const sessionDataService = createSessionDataService(db);
|
|
|
|
|
const svc = disposables.add(new AgentService(new NullLogService(), fileService, sessionDataService, { _serviceBrand: undefined } as IProductService, gitService));
|
|
|
|
|
svc.configurationService.updateRootConfig({ [AgentHostShowExternalSessionsConfigKey]: AgentHostExternalSessionsMode.All });
|
|
|
|
|
svc.configurationService.updateRootConfig({ [AgentHostShowExternalSessionsConfigKey]: AgentHostExternalSessionsMode.Last30Days });
|
|
|
|
|
svc.setWorktreeIsolation(disposables.add(new WorktreeIsolation(
|
|
|
|
|
{ generateBranchName: async () => 'agents/test' },
|
|
|
|
|
gitService,
|
|
|
|
@@ -5028,7 +5080,7 @@ suite('AgentService (node dispatcher)', () => {
|
|
|
|
|
gitService.getDefaultBranch = async () => ({ name: 'main', startPoint: 'main' });
|
|
|
|
|
const sessionDataService = createSessionDataService(db);
|
|
|
|
|
const svc = disposables.add(new AgentService(new NullLogService(), fileService, sessionDataService, { _serviceBrand: undefined } as IProductService, gitService));
|
|
|
|
|
svc.configurationService.updateRootConfig({ [AgentHostShowExternalSessionsConfigKey]: AgentHostExternalSessionsMode.All });
|
|
|
|
|
svc.configurationService.updateRootConfig({ [AgentHostShowExternalSessionsConfigKey]: AgentHostExternalSessionsMode.Last30Days });
|
|
|
|
|
svc.setWorktreeIsolation(disposables.add(new WorktreeIsolation(
|
|
|
|
|
{ generateBranchName: async () => 'agents/test' },
|
|
|
|
|
gitService,
|
|
|
|
@@ -5189,7 +5241,7 @@ suite('AgentService (node dispatcher)', () => {
|
|
|
|
|
disposables.add(toDisposable(() => agent.dispose()));
|
|
|
|
|
agent.resolvedWorkingDirectory = URI.file('/original');
|
|
|
|
|
const { session } = await createAgentSession(agent);
|
|
|
|
|
setExternalSessionsMode(service, AgentHostExternalSessionsMode.All, 1);
|
|
|
|
|
setExternalSessionsMode(service, AgentHostExternalSessionsMode.Last30Days, 1);
|
|
|
|
|
await waitForSessionListReconciliation(service);
|
|
|
|
|
service.registerProvider(agent);
|
|
|
|
|
agent.fireDiscoveredChats([discoveredChat(session)]);
|
|
|
|
@@ -5199,13 +5251,14 @@ suite('AgentService (node dispatcher)', () => {
|
|
|
|
|
|
|
|
|
|
const listing = service.listSessions();
|
|
|
|
|
await agent.listStarted.p;
|
|
|
|
|
const summaryNow = Date.now();
|
|
|
|
|
service.stateManager.restoreSession({
|
|
|
|
|
resource: session.toString(),
|
|
|
|
|
provider: 'copilot',
|
|
|
|
|
title: 'Materialized',
|
|
|
|
|
status: SessionStatus.Idle,
|
|
|
|
|
createdAt: new Date(1000).toISOString(),
|
|
|
|
|
modifiedAt: new Date(2000).toISOString(),
|
|
|
|
|
createdAt: new Date(summaryNow - 1_000).toISOString(),
|
|
|
|
|
modifiedAt: new Date(summaryNow).toISOString(),
|
|
|
|
|
project: { uri: URI.file('/project').toString(), displayName: 'project' },
|
|
|
|
|
workingDirectories: [URI.file('/worktree').toString()],
|
|
|
|
|
}, []);
|
|
|
|
@@ -5217,7 +5270,7 @@ suite('AgentService (node dispatcher)', () => {
|
|
|
|
|
project: listed?.project && { uri: listed.project.uri.path, displayName: listed.project.displayName },
|
|
|
|
|
workingDirectory: listed?.workingDirectories?.[0]?.path,
|
|
|
|
|
}, {
|
|
|
|
|
modifiedTime: 2000,
|
|
|
|
|
modifiedTime: summaryNow,
|
|
|
|
|
project: { uri: '/project', displayName: 'project' },
|
|
|
|
|
workingDirectory: '/worktree',
|
|
|
|
|
});
|
|
|
|
|