mirror of
https://github.com/MoonshotAI/kimi-code.git
synced 2026-08-16 20:25:36 +00:00
refactor(agent-core-v2): fold session lifecycle hooks into sessionLifecycle events (#2896)
This commit is contained in:
parent
5857ba23b0
commit
1414d46028
11 changed files with 91 additions and 133 deletions
|
|
@ -416,7 +416,6 @@ export * from '#/workspace/workspaceContext/workspaceContext';
|
|||
export * from '#/workspace/sessionLifecycle/sessionLifecycle';
|
||||
export * from '#/workspace/sessionLifecycle/sessionLifecycleService';
|
||||
export * from '#/workspace/sessionLifecycle/internal/addressing';
|
||||
export * from '#/session/sessionLifecycleHooks/sessionLifecycleHooks';
|
||||
export * from '#/session/externalHooks/externalHooks';
|
||||
export * from '#/session/externalHooks/externalHooksService';
|
||||
import '#/app/sessionExport/errors';
|
||||
|
|
|
|||
|
|
@ -28,24 +28,22 @@ import { LifecycleScope } from '#/app/scopes';
|
|||
import { ScopeActivation, registerScopedService } from '#/_base/di/scope';
|
||||
import { IntervalTimer } from '#/_base/utils/timer';
|
||||
import { IExternalHooksRunnerService } from '#/app/externalHooksRunner/externalHooksRunner';
|
||||
import type { Hooks } from '#/hooks';
|
||||
import { IModelService } from '#/kosong/model/model';
|
||||
import {
|
||||
ISessionAgentProfileCatalog,
|
||||
} from '#/session/sessionAgentProfileCatalog/sessionAgentProfileCatalog';
|
||||
import { ISessionContext } from '#/session/sessionContext/sessionContext';
|
||||
import {
|
||||
ISessionLifecycleHooks,
|
||||
type SessionCloseReason,
|
||||
type SessionCreateSource,
|
||||
type SessionLifecycleHookSlots,
|
||||
} from '#/session/sessionLifecycleHooks/sessionLifecycleHooks';
|
||||
import { ISessionMetadata } from '#/session/sessionMetadata/sessionMetadata';
|
||||
import {
|
||||
type AgentTaskStartHookContext,
|
||||
type AgentTaskStopHookContext,
|
||||
ISessionSubagentService,
|
||||
} from '#/session/subagent/subagent';
|
||||
import {
|
||||
ISessionLifecycleService,
|
||||
type SessionCloseReason,
|
||||
type SessionCreateSource,
|
||||
} from '#/workspace/sessionLifecycle/sessionLifecycle';
|
||||
|
||||
import { ISessionExternalHooksService } from './externalHooks';
|
||||
|
||||
|
|
@ -64,7 +62,7 @@ export class SessionExternalHooksService
|
|||
|
||||
constructor(
|
||||
@ISessionContext private readonly context: ISessionContext,
|
||||
@ISessionLifecycleHooks lifecycleHooks: Hooks<SessionLifecycleHookSlots>,
|
||||
@ISessionLifecycleService lifecycle: ISessionLifecycleService,
|
||||
@ISessionSubagentService subagents: ISessionSubagentService,
|
||||
@ISessionMetadata private readonly metadata: ISessionMetadata,
|
||||
@ISessionAgentProfileCatalog private readonly profiles: ISessionAgentProfileCatalog,
|
||||
|
|
@ -90,17 +88,17 @@ export class SessionExternalHooksService
|
|||
}),
|
||||
);
|
||||
this._register(
|
||||
lifecycleHooks.onDidCreateSession.register('externalHooks', async (event, next) => {
|
||||
lifecycle.onDidCreateSession((event) => {
|
||||
if (event.sessionId !== this.context.sessionId) return;
|
||||
if (event.source !== 'fork') {
|
||||
await this.triggerSessionStart(event.source);
|
||||
event.waitUntil(this.triggerSessionStart(event.source));
|
||||
}
|
||||
await next();
|
||||
}),
|
||||
);
|
||||
this._register(
|
||||
lifecycleHooks.onWillCloseSession.register('externalHooks', async (event, next) => {
|
||||
await this.triggerSessionEnd(event.reason);
|
||||
await next();
|
||||
lifecycle.onWillCloseSession((event) => {
|
||||
if (event.sessionId !== this.context.sessionId) return;
|
||||
event.waitUntil(this.triggerSessionEnd(event.reason));
|
||||
}),
|
||||
);
|
||||
this._register(
|
||||
|
|
|
|||
|
|
@ -1,36 +0,0 @@
|
|||
/**
|
||||
* `sessionLifecycleHooks` domain — per-session lifecycle hook slots.
|
||||
*
|
||||
* Defines the `ISessionLifecycleHooks` seed: one ordered hook-slots instance
|
||||
* per session, with slots around the session's create (`onDidCreateSession`)
|
||||
* and close (`onWillCloseSession`). Also owns the shared
|
||||
* `SessionCreateSource` / `SessionCloseReason` vocabulary.
|
||||
*/
|
||||
|
||||
import { createDecorator, type ServiceIdentifier } from '#/_base/di/instantiation';
|
||||
import type { ScopeSeed } from '#/_base/di/scope';
|
||||
import type { Hooks } from '#/hooks';
|
||||
|
||||
export type SessionCreateSource = 'startup' | 'resume' | 'fork';
|
||||
|
||||
export type SessionCloseReason = 'exit' | 'archive';
|
||||
|
||||
export interface SessionStartHookEvent {
|
||||
readonly source: SessionCreateSource;
|
||||
}
|
||||
|
||||
export interface SessionEndHookEvent {
|
||||
readonly reason: SessionCloseReason;
|
||||
}
|
||||
|
||||
export type SessionLifecycleHookSlots = {
|
||||
readonly onDidCreateSession: SessionStartHookEvent;
|
||||
readonly onWillCloseSession: SessionEndHookEvent;
|
||||
};
|
||||
|
||||
export const ISessionLifecycleHooks: ServiceIdentifier<Hooks<SessionLifecycleHookSlots>> =
|
||||
createDecorator<Hooks<SessionLifecycleHookSlots>>('sessionLifecycleHooks');
|
||||
|
||||
export function sessionLifecycleHooksSeed(hooks: Hooks<SessionLifecycleHookSlots>): ScopeSeed {
|
||||
return [[ISessionLifecycleHooks as ServiceIdentifier<unknown>, hooks]];
|
||||
}
|
||||
|
|
@ -24,15 +24,13 @@
|
|||
|
||||
import { createDecorator, type ServiceIdentifier } from '#/_base/di/instantiation';
|
||||
import type { ISessionScopeHandle } from '#/_base/di/scope';
|
||||
import type { Event } from '#/_base/event';
|
||||
import { type Event, type IWaitUntil } from '#/_base/event';
|
||||
import type { BindAgentInput } from '#/agent/profile/profile';
|
||||
import type { McpServerConfig } from '#/mcpCore/config-schema';
|
||||
import type {
|
||||
SessionCloseReason,
|
||||
SessionCreateSource,
|
||||
} from '#/session/sessionLifecycleHooks/sessionLifecycleHooks';
|
||||
|
||||
export type { SessionCloseReason, SessionCreateSource };
|
||||
export type SessionCreateSource = 'startup' | 'resume' | 'fork';
|
||||
|
||||
export type SessionCloseReason = 'exit' | 'archive';
|
||||
|
||||
export interface CreateSessionOptions {
|
||||
readonly sessionId?: string;
|
||||
|
|
@ -124,7 +122,8 @@ export interface ISessionLifecycleService {
|
|||
readonly _serviceBrand: undefined;
|
||||
|
||||
readonly onWillCreateSession: Event<SessionWillCreateEvent>;
|
||||
readonly onDidCreateSession: Event<SessionCreatedEvent>;
|
||||
readonly onDidCreateSession: Event<SessionCreatedEvent & IWaitUntil>;
|
||||
readonly onWillCloseSession: Event<SessionWillCloseEvent & IWaitUntil>;
|
||||
readonly onDidCloseSession: Event<SessionClosedEvent>;
|
||||
readonly onDidArchiveSession: Event<SessionArchivedEvent>;
|
||||
readonly onDidForkSession: Event<SessionForkedEvent>;
|
||||
|
|
|
|||
|
|
@ -101,7 +101,7 @@ import {
|
|||
registerScopedService,
|
||||
} from '#/_base/di/scope';
|
||||
import { unwrapErrorCause } from '#/_base/errors/errors';
|
||||
import { Emitter, type Event } from '#/_base/event';
|
||||
import { AsyncEmitter, Emitter, type Event, type IWaitUntil } from '#/_base/event';
|
||||
import { DEFAULT_PLAN_MODE_SECTION } from '#/features/plan/configSection';
|
||||
import { IAgentPlanService } from '#/features/plan/plan';
|
||||
import { IBootstrapService } from '#/app/bootstrap/bootstrap';
|
||||
|
|
@ -118,7 +118,6 @@ import {
|
|||
} from '#/app/sessionIndex/sessionIndex';
|
||||
import { ITelemetryService } from '#/app/telemetry/telemetry';
|
||||
import { ErrorCodes, Error2, isError2 } from '#/errors';
|
||||
import { createHooks } from '#/hooks';
|
||||
import { IHostEnvironment } from '#/os/interface/hostEnvironment';
|
||||
import { IHostFileSystem, type HostDirEntry } from '#/os/interface/hostFileSystem';
|
||||
import { IAppendLogStore } from '#/persistence/interface/appendLogStore';
|
||||
|
|
@ -130,11 +129,6 @@ import { ISessionContext, sessionContextSeed } from '#/session/sessionContext/se
|
|||
import { sessionEphemeralMcpServersSeed } from '#/session/mcp/ephemeralMcpServers';
|
||||
import { sessionAgentProfileCatalogSeed } from '#/session/sessionAgentProfileCatalog/agentProfileCatalogSeed';
|
||||
import { installSessionSeedAdapters } from '#/session/sessionSeed/sessionSeedAdapters';
|
||||
import {
|
||||
ISessionLifecycleHooks,
|
||||
sessionLifecycleHooksSeed,
|
||||
type SessionLifecycleHookSlots,
|
||||
} from '#/session/sessionLifecycleHooks/sessionLifecycleHooks';
|
||||
import { ISessionMetadata, type SessionMeta } from '#/session/sessionMetadata/sessionMetadata';
|
||||
import { drainSessionMetadataWrites, toEpochMs } from '#/session/sessionMetadata/sessionMetadataService';
|
||||
import { ISessionProcessRunner } from '#/session/process/processRunner';
|
||||
|
|
@ -183,6 +177,8 @@ type MaterializeSessionOptions = Omit<CreateSessionOptions, 'sessionId'> & {
|
|||
readonly sessionId: string;
|
||||
};
|
||||
|
||||
const NO_ABORT = new AbortController().signal;
|
||||
|
||||
// NOTE: stays Disposable — its own 'get' and 'config' collide with the Fiber
|
||||
export class SessionLifecycleService extends Disposable implements ISessionLifecycleService {
|
||||
declare readonly _serviceBrand: undefined;
|
||||
|
|
@ -192,8 +188,16 @@ export class SessionLifecycleService extends Disposable implements ISessionLifec
|
|||
);
|
||||
readonly onWillCreateSession: Event<SessionWillCreateEvent> =
|
||||
this._onWillCreateSession.event;
|
||||
private readonly _onDidCreateSession = this._register(new Emitter<SessionCreatedEvent>());
|
||||
readonly onDidCreateSession: Event<SessionCreatedEvent> = this._onDidCreateSession.event;
|
||||
private readonly _onDidCreateSession = this._register(
|
||||
new AsyncEmitter<SessionCreatedEvent & IWaitUntil>(),
|
||||
);
|
||||
readonly onDidCreateSession: Event<SessionCreatedEvent & IWaitUntil> =
|
||||
this._onDidCreateSession.event;
|
||||
private readonly _onWillCloseSession = this._register(
|
||||
new AsyncEmitter<SessionWillCloseEvent & IWaitUntil>(),
|
||||
);
|
||||
readonly onWillCloseSession: Event<SessionWillCloseEvent & IWaitUntil> =
|
||||
this._onWillCloseSession.event;
|
||||
private readonly _onDidCloseSession = this._register(new Emitter<SessionClosedEvent>());
|
||||
readonly onDidCloseSession: Event<SessionClosedEvent> = this._onDidCloseSession.event;
|
||||
private readonly _onDidArchiveSession = this._register(new Emitter<SessionArchivedEvent>());
|
||||
|
|
@ -295,10 +299,6 @@ export class SessionLifecycleService extends Disposable implements ISessionLifec
|
|||
scope: (subKey?: string): string =>
|
||||
subKey === undefined || subKey === '' ? sessionScope : `${sessionScope}/${subKey}`,
|
||||
};
|
||||
const hooks = createHooks<SessionLifecycleHookSlots, keyof SessionLifecycleHookSlots>([
|
||||
'onDidCreateSession',
|
||||
'onWillCloseSession',
|
||||
]);
|
||||
await this.hostEnv.ready;
|
||||
const handle = createScopedChildHandle(
|
||||
this.instantiation,
|
||||
|
|
@ -307,7 +307,6 @@ export class SessionLifecycleService extends Disposable implements ISessionLifec
|
|||
{
|
||||
seeds: [
|
||||
...sessionContextSeed(ctx),
|
||||
...sessionLifecycleHooksSeed(hooks),
|
||||
[ITelemetryService, this.telemetry.withContext({ sessionId: opts.sessionId })],
|
||||
...sessionAgentProfileCatalogSeed({
|
||||
_serviceBrand: undefined,
|
||||
|
|
@ -364,10 +363,7 @@ export class SessionLifecycleService extends Disposable implements ISessionLifec
|
|||
}
|
||||
|
||||
private async announceCreated(event: SessionCreatedEvent): Promise<void> {
|
||||
await event.handle.accessor
|
||||
.get(ISessionLifecycleHooks)
|
||||
.onDidCreateSession.run({ source: event.source });
|
||||
this._onDidCreateSession.fire(event);
|
||||
await this._onDidCreateSession.fireAsync(event, NO_ABORT);
|
||||
event.handle.accessor
|
||||
.get(ITelemetryService)
|
||||
.track2('session_started', { resumed: event.source === 'resume' });
|
||||
|
|
@ -491,9 +487,7 @@ export class SessionLifecycleService extends Disposable implements ISessionLifec
|
|||
}
|
||||
|
||||
private async announceWillClose(event: SessionWillCloseEvent): Promise<void> {
|
||||
await event.handle.accessor
|
||||
.get(ISessionLifecycleHooks)
|
||||
.onWillCloseSession.run({ reason: event.reason });
|
||||
await this._onWillCloseSession.fireAsync(event, NO_ABORT);
|
||||
}
|
||||
|
||||
private async drainAgents(handle: ISessionScopeHandle): Promise<void> {
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ import {
|
|||
createServices,
|
||||
type TestInstantiationService,
|
||||
} from '#/_base/di/test';
|
||||
import { Emitter, Event } from '#/_base/event';
|
||||
import { AsyncEmitter, Emitter, Event, type IWaitUntil } from '#/_base/event';
|
||||
import { emptyUsage } from '#/kosong/contract/usage';
|
||||
import { buildContextCompactionShape } from '#/agent/contextMemory/compactionHandoff';
|
||||
import {
|
||||
|
|
@ -47,10 +47,13 @@ import { IPluginService } from '#/app/plugin/plugin';
|
|||
import { IHostProcessService } from '#/os/interface/hostProcess';
|
||||
import { HostProcessService } from '#/os/backends/node-local/hostProcessService';
|
||||
import {
|
||||
ISessionLifecycleHooks,
|
||||
type SessionLifecycleHookSlots,
|
||||
} from '#/session/sessionLifecycleHooks/sessionLifecycleHooks';
|
||||
import { createHooks, type Hooks } from '#/hooks';
|
||||
ISessionLifecycleService,
|
||||
type SessionCloseReason,
|
||||
type SessionCreatedEvent,
|
||||
type SessionCreateSource,
|
||||
type SessionWillCloseEvent,
|
||||
} from '#/workspace/sessionLifecycle/sessionLifecycle';
|
||||
import { createHooks } from '#/hooks';
|
||||
import { ISessionContext } from '#/session/sessionContext/sessionContext';
|
||||
import {
|
||||
type AgentTaskHooks,
|
||||
|
|
@ -229,11 +232,21 @@ function stubSessionContext(): ISessionContext {
|
|||
};
|
||||
}
|
||||
|
||||
function stubSessionLifecycleHooks(): Hooks<SessionLifecycleHookSlots> {
|
||||
return createHooks<SessionLifecycleHookSlots, keyof SessionLifecycleHookSlots>([
|
||||
'onDidCreateSession',
|
||||
'onWillCloseSession',
|
||||
]);
|
||||
function stubSessionLifecycle() {
|
||||
const didCreate = new AsyncEmitter<SessionCreatedEvent & IWaitUntil>();
|
||||
const willClose = new AsyncEmitter<SessionWillCloseEvent & IWaitUntil>();
|
||||
const noAbort = new AbortController().signal;
|
||||
const handle = {} as ISessionScopeHandle;
|
||||
return {
|
||||
service: {
|
||||
onDidCreateSession: didCreate.event,
|
||||
onWillCloseSession: willClose.event,
|
||||
},
|
||||
fireDidCreate: (source: SessionCreateSource): Promise<void> =>
|
||||
didCreate.fireAsync({ sessionId: 'session-1', handle, source }, noAbort),
|
||||
fireWillClose: (reason: SessionCloseReason): Promise<void> =>
|
||||
willClose.fireAsync({ sessionId: 'session-1', handle, reason }, noAbort),
|
||||
};
|
||||
}
|
||||
|
||||
describe('IExternalHooksRunnerService integration', () => {
|
||||
|
|
@ -539,7 +552,7 @@ describe('IExternalHooksRunnerService integration', () => {
|
|||
? 'sessions/workspace-1/session-1'
|
||||
: `sessions/workspace-1/session-1/${subKey}`,
|
||||
});
|
||||
reg.defineInstance(ISessionLifecycleHooks, stubSessionLifecycleHooks());
|
||||
reg.definePartialInstance(ISessionLifecycleService, stubSessionLifecycle().service);
|
||||
reg.defineInstance(ISessionMetadata, stubSessionMetadata());
|
||||
reg.defineInstance(ISessionAgentProfileCatalog, stubProfileCatalog());
|
||||
reg.defineInstance(IModelService, stubModelService());
|
||||
|
|
@ -856,7 +869,7 @@ describe('IExternalHooksRunnerService integration', () => {
|
|||
const disposables = new DisposableStore();
|
||||
let ix: TestInstantiationService | undefined;
|
||||
try {
|
||||
const lifecycleHooks = stubSessionLifecycleHooks();
|
||||
const lifecycle = stubSessionLifecycle();
|
||||
const path = hookLogPath();
|
||||
const command = appendHookLogCommand(path);
|
||||
const cwd = mkdtempSync(join(tmpdir(), 'session-external-hooks-cwd-'));
|
||||
|
|
@ -877,7 +890,7 @@ describe('IExternalHooksRunnerService integration', () => {
|
|||
? 'sessions/workspace-1/session-1'
|
||||
: `sessions/workspace-1/session-1/${subKey}`,
|
||||
});
|
||||
reg.defineInstance(ISessionLifecycleHooks, lifecycleHooks);
|
||||
reg.definePartialInstance(ISessionLifecycleService, lifecycle.service);
|
||||
reg.defineInstance(ISessionMetadata, stubSessionMetadata());
|
||||
reg.defineInstance(ISessionAgentProfileCatalog, stubProfileCatalog());
|
||||
reg.defineInstance(IModelService, stubModelService());
|
||||
|
|
@ -907,11 +920,11 @@ describe('IExternalHooksRunnerService integration', () => {
|
|||
ix.set(ISessionExternalHooksService, new SyncDescriptor(SessionExternalHooksService));
|
||||
ix.get(ISessionExternalHooksService);
|
||||
|
||||
await lifecycleHooks.onDidCreateSession.run({ source: 'startup' });
|
||||
await lifecycleHooks.onDidCreateSession.run({ source: 'resume' });
|
||||
await lifecycleHooks.onDidCreateSession.run({ source: 'fork' });
|
||||
await lifecycleHooks.onWillCloseSession.run({ reason: 'exit' });
|
||||
await lifecycleHooks.onWillCloseSession.run({ reason: 'archive' });
|
||||
await lifecycle.fireDidCreate('startup');
|
||||
await lifecycle.fireDidCreate('resume');
|
||||
await lifecycle.fireDidCreate('fork');
|
||||
await lifecycle.fireWillClose('exit');
|
||||
await lifecycle.fireWillClose('archive');
|
||||
|
||||
expect(readHookLog(path)).toEqual([
|
||||
{
|
||||
|
|
@ -1053,7 +1066,7 @@ describe('IExternalHooksRunnerService integration', () => {
|
|||
const disposables = new DisposableStore();
|
||||
let ix: TestInstantiationService | undefined;
|
||||
try {
|
||||
const lifecycleHooks = stubSessionLifecycleHooks();
|
||||
const lifecycle = stubSessionLifecycle();
|
||||
const path = hookLogPath();
|
||||
const command = stdinScript([
|
||||
'const fs = require("node:fs");',
|
||||
|
|
@ -1074,7 +1087,7 @@ describe('IExternalHooksRunnerService integration', () => {
|
|||
additionalServices: (reg) => {
|
||||
registerStateServices(reg);
|
||||
reg.defineInstance(ISessionContext, stubSessionContext());
|
||||
reg.defineInstance(ISessionLifecycleHooks, lifecycleHooks);
|
||||
reg.definePartialInstance(ISessionLifecycleService, lifecycle.service);
|
||||
reg.defineInstance(ISessionMetadata, stubSessionMetadata('My Session'));
|
||||
reg.defineInstance(ISessionAgentProfileCatalog, stubProfileCatalog('coder'));
|
||||
reg.defineInstance(IModelService, stubModelService('kimi-k2'));
|
||||
|
|
@ -1102,7 +1115,7 @@ describe('IExternalHooksRunnerService integration', () => {
|
|||
ix.get(ISessionExternalHooksService);
|
||||
await flushMicrotasks();
|
||||
|
||||
await lifecycleHooks.onDidCreateSession.run({ source: 'startup' });
|
||||
await lifecycle.fireDidCreate('startup');
|
||||
|
||||
expect(readHookLog(path)).toEqual([
|
||||
{
|
||||
|
|
@ -1261,7 +1274,7 @@ describe('IExternalHooksRunnerService integration', () => {
|
|||
additionalServices: (reg) => {
|
||||
registerStateServices(reg);
|
||||
reg.defineInstance(ISessionContext, stubSessionContext());
|
||||
reg.defineInstance(ISessionLifecycleHooks, stubSessionLifecycleHooks());
|
||||
reg.definePartialInstance(ISessionLifecycleService, stubSessionLifecycle().service);
|
||||
reg.defineInstance(ISessionMetadata, stubSessionMetadata());
|
||||
reg.defineInstance(ISessionAgentProfileCatalog, stubProfileCatalog());
|
||||
reg.defineInstance(IModelService, stubModelService());
|
||||
|
|
@ -1306,7 +1319,7 @@ describe('IExternalHooksRunnerService integration', () => {
|
|||
additionalServices: (reg) => {
|
||||
registerStateServices(reg);
|
||||
reg.defineInstance(ISessionContext, stubSessionContext());
|
||||
reg.defineInstance(ISessionLifecycleHooks, stubSessionLifecycleHooks());
|
||||
reg.definePartialInstance(ISessionLifecycleService, stubSessionLifecycle().service);
|
||||
reg.defineInstance(ISessionMetadata, stubSessionMetadata());
|
||||
reg.defineInstance(ISessionAgentProfileCatalog, stubProfileCatalog());
|
||||
reg.defineInstance(IModelService, stubModelService());
|
||||
|
|
@ -1353,7 +1366,7 @@ describe('IExternalHooksRunnerService integration', () => {
|
|||
additionalServices: (reg) => {
|
||||
registerStateServices(reg);
|
||||
reg.defineInstance(ISessionContext, stubSessionContext());
|
||||
reg.defineInstance(ISessionLifecycleHooks, stubSessionLifecycleHooks());
|
||||
reg.definePartialInstance(ISessionLifecycleService, stubSessionLifecycle().service);
|
||||
reg.defineInstance(ISessionMetadata, stubSessionMetadata());
|
||||
reg.defineInstance(ISessionAgentProfileCatalog, stubProfileCatalog());
|
||||
reg.defineInstance(IModelService, stubModelService());
|
||||
|
|
|
|||
|
|
@ -95,6 +95,7 @@ describe('RestGateway', () => {
|
|||
_serviceBrand: undefined,
|
||||
onWillCreateSession: () => ({ dispose: () => {} }),
|
||||
onDidCreateSession: () => ({ dispose: () => {} }),
|
||||
onWillCloseSession: () => ({ dispose: () => {} }),
|
||||
onDidCloseSession: () => ({ dispose: () => {} }),
|
||||
onDidArchiveSession: () => ({ dispose: () => {} }),
|
||||
onDidForkSession: () => ({ dispose: () => {} }),
|
||||
|
|
|
|||
|
|
@ -918,6 +918,7 @@ function registerSessionExportServices(
|
|||
_serviceBrand: undefined,
|
||||
onWillCreateSession: noopEvent,
|
||||
onDidCreateSession: noopEvent,
|
||||
onWillCloseSession: noopEvent,
|
||||
onDidCloseSession: noopEvent,
|
||||
onDidArchiveSession: noopEvent,
|
||||
onDidForkSession: noopEvent,
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ import { toDisposable } from '#/_base/di/lifecycle';
|
|||
import type { IInstantiationService } from '#/_base/di/instantiation';
|
||||
import type { IAgentScopeHandle } from '#/_base/di/scope';
|
||||
import { IFeatureManager } from '#/app/feature/featureManager';
|
||||
import { Emitter, Event } from '#/_base/event';
|
||||
import { Emitter, Event, type IWaitUntil } from '#/_base/event';
|
||||
import { IAgentLifecycleService } from '#/session/agentLifecycle/agentLifecycle';
|
||||
import type { Promisable, PromisifyMethods } from '#/_base/utils/types';
|
||||
import type { AgentTaskInfo } from '#/agent/task/task';
|
||||
|
|
@ -172,11 +172,11 @@ import {
|
|||
type ScopeSeed,
|
||||
type ServiceIdentifier,
|
||||
} from '#/index';
|
||||
import { createHooks } from '#/hooks';
|
||||
import {
|
||||
ISessionLifecycleHooks,
|
||||
type SessionLifecycleHookSlots,
|
||||
} from '#/session/sessionLifecycleHooks/sessionLifecycleHooks';
|
||||
ISessionLifecycleService,
|
||||
type SessionCreatedEvent,
|
||||
type SessionWillCloseEvent,
|
||||
} from '#/workspace/sessionLifecycle/sessionLifecycle';
|
||||
import { IEventBus } from '#/app/event/eventBus';
|
||||
import { IWireService } from '#/wire/wire';
|
||||
import { WireService } from '#/wire/wireService';
|
||||
|
|
@ -1209,13 +1209,10 @@ export class AgentTestContext {
|
|||
scope: (subKey?: string): string =>
|
||||
subKey === undefined || subKey === '' ? sessionScope : `${sessionScope}/${subKey}`,
|
||||
});
|
||||
reg.defineInstance(
|
||||
ISessionLifecycleHooks,
|
||||
createHooks<SessionLifecycleHookSlots, keyof SessionLifecycleHookSlots>([
|
||||
'onDidCreateSession',
|
||||
'onWillCloseSession',
|
||||
]),
|
||||
);
|
||||
reg.definePartialInstance(ISessionLifecycleService, {
|
||||
onDidCreateSession: Event.None as Event<SessionCreatedEvent & IWaitUntil>,
|
||||
onWillCloseSession: Event.None as Event<SessionWillCloseEvent & IWaitUntil>,
|
||||
});
|
||||
reg.defineInstance(ISessionInteractionService, this.createInteractionService());
|
||||
reg.defineInstance(ISessionApprovalService, this.createApprovalService());
|
||||
reg.defineInstance(ISessionQuestionService, this.createQuestionService());
|
||||
|
|
|
|||
|
|
@ -15,7 +15,6 @@ import {
|
|||
import { type ScopedTestHost, createScopedTestHost, stubPair } from '#/_base/di/test';
|
||||
import { Event } from '#/_base/event';
|
||||
import { ILogService } from '#/_base/log/log';
|
||||
import type { Hooks } from '#/hooks';
|
||||
import { IBootstrapService } from '#/app/bootstrap/bootstrap';
|
||||
import { IConfigService } from '#/app/config/config';
|
||||
import { IFlagService } from '#/app/flag/flag';
|
||||
|
|
@ -59,10 +58,6 @@ import { IWorkspaceToolPolicy } from '#/workspace/workspaceToolPolicy/workspaceT
|
|||
import { WorkspaceToolPolicyService } from '#/workspace/workspaceToolPolicy/workspaceToolPolicyService';
|
||||
import { IAgentActivityView } from '#/agent/activityView/activityView';
|
||||
import { ISessionExternalHooksService } from '#/session/externalHooks/externalHooks';
|
||||
import {
|
||||
ISessionLifecycleHooks,
|
||||
type SessionLifecycleHookSlots,
|
||||
} from '#/session/sessionLifecycleHooks/sessionLifecycleHooks';
|
||||
import { ISessionIndexMirror } from '#/app/sessionIndex/sessionIndex';
|
||||
import {
|
||||
ISessionMetadata,
|
||||
|
|
@ -521,19 +516,19 @@ class RecordingSessionExternalHooksService
|
|||
|
||||
constructor(
|
||||
@ISessionContext private readonly context: ISessionContext,
|
||||
@ISessionLifecycleHooks hooks: Hooks<SessionLifecycleHookSlots>,
|
||||
@ISessionLifecycleService lifecycle: ISessionLifecycleService,
|
||||
) {
|
||||
super();
|
||||
this._register(
|
||||
hooks.onDidCreateSession.register('test', async (event, next) => {
|
||||
lifecycle.onDidCreateSession((event) => {
|
||||
if (event.sessionId !== this.context.sessionId) return;
|
||||
recordedSessionHookEvents.push(`create:${event.source}:${this.context.sessionId}`);
|
||||
await next();
|
||||
}),
|
||||
);
|
||||
this._register(
|
||||
hooks.onWillCloseSession.register('test', async (event, next) => {
|
||||
lifecycle.onWillCloseSession((event) => {
|
||||
if (event.sessionId !== this.context.sessionId) return;
|
||||
recordedSessionHookEvents.push(`close:${event.reason}:${this.context.sessionId}`);
|
||||
await next();
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -34,7 +34,6 @@ import {
|
|||
drainSessionIndexMirror,
|
||||
HostProcessError,
|
||||
IHostRequestHeaders,
|
||||
ISessionLifecycleHooks,
|
||||
ISessionLifecycleService,
|
||||
IWorkspaceLifecycleService,
|
||||
OsProcessErrors,
|
||||
|
|
@ -442,13 +441,11 @@ key = "${titleOAuthRef.key}"
|
|||
const closeGate = new Promise<void>((resolve) => {
|
||||
openCloseGate = resolve;
|
||||
});
|
||||
tempHandle!.accessor
|
||||
.get(ISessionLifecycleHooks)
|
||||
.onWillCloseSession.register('test-block', async (_event, next) => {
|
||||
markCloseStarted();
|
||||
await closeGate;
|
||||
await next();
|
||||
});
|
||||
handler.accessor.get(ISessionLifecycleService).onWillCloseSession((event) => {
|
||||
if (event.sessionId !== 'ses_title_race') return;
|
||||
markCloseStarted();
|
||||
event.waitUntil(closeGate);
|
||||
});
|
||||
|
||||
resolveFetch(
|
||||
new Response(JSON.stringify({ title: 'Generated title' }), {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue