File size: 4,374 Bytes
68d7816
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
import { createDecorator, type ServiceIdentifier } from '#/_base/di/instantiation';
import { ScopeActivation, registerScopedService } from '#/_base/di/scope';
import { Emitter, type Event } from '#/_base/event';
import type { IDisposable } from '#/_base/di/lifecycle';
import { LifecycleScope } from '#/app/scopes';
import type { Runtime, RuntimeBinding, RuntimeCapability, RuntimeLease } from '#/runtime/runtime';
import { runtimeStatusAllows, type RuntimeGenerationSnapshot } from '#/runtime/runtimeRegistry';
import {
  IRuntimeResolver,
  IWorkspaceInstanceManager,
} from '#/workspace/workspaceInstance/workspaceInstanceManager';

import { IAgentRuntimeBindingService } from './runtimeBinding';

export interface AgentRuntimeBindingSnapshot {
  readonly binding: RuntimeBinding;
  readonly available: boolean;
  readonly runtime?: RuntimeGenerationSnapshot;
}

export interface IAgentRuntimeService {
  readonly _serviceBrand: undefined;
  readonly onDidChange: Event<void>;
  inspect(): Runtime;
  isAvailable(required?: readonly RuntimeCapability[]): boolean;
  acquire(required?: readonly RuntimeCapability[]): RuntimeLease;
}

export const IAgentRuntimeService: ServiceIdentifier<IAgentRuntimeService> =
  createDecorator<IAgentRuntimeService>('agentRuntimeService');

export function inspectAgentRuntime(service: IAgentRuntimeService): Runtime {
  return service.inspect();
}

export function snapshotAgentRuntimeBinding(
  bindingService: IAgentRuntimeBindingService,
  runtimeService: IAgentRuntimeService,
): AgentRuntimeBindingSnapshot {
  const binding = bindingService.current;
  try {
    const runtime = runtimeService.inspect();
    return {
      binding,
      available: runtimeService.isAvailable(),
      runtime: {
        runtimeId: runtime.identity.runtimeId,
        generation: runtime.identity.generation,
        status: runtime.status,
        capabilities: [...runtime.capabilities],
      },
    };
  } catch {
    return { binding, available: false };
  }
}

export class AgentRuntimeService implements IAgentRuntimeService {
  declare readonly _serviceBrand: undefined;
  private readonly changeEmitter = new Emitter<void>();
  readonly onDidChange = this.changeEmitter.event;
  private readonly bindingSubscription: IDisposable;
  private readonly workspaceSubscription: IDisposable;
  private registrySubscription: IDisposable | undefined;

  constructor(
    @IAgentRuntimeBindingService private readonly binding: IAgentRuntimeBindingService,
    @IRuntimeResolver private readonly resolver: IRuntimeResolver,
    @IWorkspaceInstanceManager private readonly workspaces: IWorkspaceInstanceManager,
  ) {
    this.bindingSubscription = this.binding.onDidChange(() => this.rebind());
    this.workspaceSubscription = this.workspaces.onDidChange((change) => {
      if (change.workspaceId === this.binding.current.workspaceId) this.rebind();
    });
    this.bindRegistry();
  }

  inspect(): Runtime {
    return this.resolver.inspect(this.binding.current);
  }

  isAvailable(required: readonly RuntimeCapability[] = []): boolean {
    try {
      const runtime = this.inspect();
      return runtimeStatusAllows(runtime, required) && required.every((capability) => runtime.capabilities.has(capability));
    } catch {
      return false;
    }
  }

  acquire(required: readonly RuntimeCapability[] = []): RuntimeLease {
    return this.resolver.acquire(this.binding.current, required);
  }

  dispose(): void {
    this.registrySubscription?.dispose();
    this.workspaceSubscription.dispose();
    this.bindingSubscription.dispose();
    this.changeEmitter.dispose();
  }

  private rebind(): void {
    this.bindRegistry();
    this.changeEmitter.fire();
  }

  private bindRegistry(): void {
    this.registrySubscription?.dispose();
    const binding = this.binding.current;
    const workspace = this.workspaces.get(binding.workspaceId);
    this.registrySubscription = workspace?.runtimes.onDidChange((change) => {
      if (change.runtimeId !== this.binding.current.runtimeId) return;
      const current = workspace.runtimes.current(change.runtimeId);
      if (change.current !== undefined && change.current !== current) return;
      this.changeEmitter.fire();
    });
  }
}

registerScopedService(
  LifecycleScope.Agent,
  IAgentRuntimeService,
  AgentRuntimeService,
  ScopeActivation.OnDemand,
  'agentRuntimeBinding',
);